mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 23:47:46 +00:00
feat: condaenv & full docker env (#668)
* use conda to run kaggle and mlebench code * refactor: Simplify environment configuration and execution logic * add setting to use local env in ds * refine dockerfile * fix: Move MLEBDockerEnv initialization inside conditionals & fix condaenv * refactor: reformat code for better readability and consistency * feat: add conda env to all envs. * fix: fix bugs when run loop * refactor: Simplify DockerEnv configuration in mle_summary.py * fix image bug * style: reformat code for better readability and consistency * change commit * feat: Add entrypoint script for sing_docker scenario in rdagent * refactor: add Any type hints and comments for clarity in env.py * feat: Create log directory if it doesn't exist in entrypoint script * feat: Add debug mode and list root directory in entrypoint script * fix: Remove specific branch checkout in Dockerfile for RD-Agent * fix: Add competition argument to loop.py script execution * fix: Correct directory navigation and dependency installation in entrypoint.sh * fix: Correct user ownership assignment in entrypoint script * refactor: Comment out redundant log copying to RD_OUTPUT_DIR * fix: Unset LOG_TRACE_PATH to prevent log contamination in entrypoint.sh --------- Co-authored-by: Xu Yang <peteryang@vip.qq.com>
This commit is contained in:
+1
-1
@@ -170,6 +170,6 @@ mlruns/
|
||||
|
||||
# shell script
|
||||
*.out
|
||||
*.sh
|
||||
/*.sh
|
||||
.aider*
|
||||
rdagent/app/benchmark/factor/example.json
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
from typing import Literal
|
||||
|
||||
from rdagent.components.coder.CoSTEER.config import CoSTEERSettings
|
||||
from rdagent.utils.env import (
|
||||
CondaConf,
|
||||
DockerEnv,
|
||||
DSDockerConf,
|
||||
Env,
|
||||
LocalEnv,
|
||||
MLEBDockerConf,
|
||||
MLECondaConf,
|
||||
)
|
||||
|
||||
|
||||
class DSCoderCoSTEERSettings(CoSTEERSettings):
|
||||
@@ -8,3 +19,32 @@ class DSCoderCoSTEERSettings(CoSTEERSettings):
|
||||
env_prefix = "DS_Coder_CoSTEER_"
|
||||
|
||||
max_seconds: int = 2400
|
||||
env_type: str = "docker"
|
||||
# TODO: extract a function for env and conf.
|
||||
|
||||
|
||||
def get_ds_env(conf_type: Literal["kaggle", "mlebench"] = "kaggle") -> Env:
|
||||
"""
|
||||
Retrieve the appropriate environment configuration based on the env_type setting.
|
||||
|
||||
Returns:
|
||||
Env: An instance of the environment configured either as DockerEnv or LocalEnv.
|
||||
|
||||
Raises:
|
||||
ValueError: If the env_type is not recognized.
|
||||
"""
|
||||
conf = DSCoderCoSTEERSettings()
|
||||
assert conf_type in ["kaggle", "mlebench"], f"Unknown conf_type: {conf_type}"
|
||||
|
||||
if conf.env_type == "docker":
|
||||
env_conf = DSDockerConf() if conf_type == "kaggle" else MLEBDockerConf()
|
||||
env = DockerEnv(conf=env_conf)
|
||||
elif conf.env_type == "conda":
|
||||
env = LocalEnv(
|
||||
conf=(
|
||||
CondaConf(conda_env_name=conf_type) if conf_type == "kaggle" else MLECondaConf(conda_env_name=conf_type)
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown env type: {conf.env_type}")
|
||||
return env
|
||||
|
||||
@@ -9,12 +9,11 @@ from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
@@ -45,11 +44,8 @@ class EnsembleCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
ds_docker_conf = DSDockerConf()
|
||||
ds_docker_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"
|
||||
}
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
fname = "test/ensemble_test.txt"
|
||||
test_code = (DIRNAME / "eval_tests" / "ensemble_test.txt").read_text()
|
||||
@@ -64,12 +60,12 @@ class EnsembleCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
)
|
||||
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
stdout, ret_code = implementation.execute_ret_code(env=de, entry=f"python {fname}")
|
||||
stdout, ret_code = implementation.execute_ret_code(env=env, entry=f"python {fname}")
|
||||
|
||||
stdout += f"\nNOTE: the above scripts run with return code {ret_code}"
|
||||
|
||||
if "main.py" in implementation.file_dict:
|
||||
workflow_stdout = implementation.execute(env=de, entry="python main.py")
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
@@ -7,12 +7,11 @@ from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
from rdagent.utils.fmt import shrink_text
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
@@ -45,22 +44,18 @@ class FeatureCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
ds_docker_conf = DSDockerConf()
|
||||
# TODO: we should /= 20 for the timeout period on debug component
|
||||
ds_docker_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"
|
||||
}
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
# TODO: do we need to clean the generated temporary content?
|
||||
fname = "test/feature_test.py"
|
||||
test_code = (DIRNAME / "eval_tests" / "feature_test.txt").read_text()
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
|
||||
stdout = implementation.execute(env=de, entry=f"python {fname}")
|
||||
stdout = implementation.execute(env=env, entry=f"python {fname}")
|
||||
|
||||
if "main.py" in implementation.file_dict:
|
||||
workflow_stdout = implementation.execute(env=de, entry="python main.py")
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
@@ -12,13 +12,13 @@ from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
ModelSingleFeedback = CoSTEERSingleFeedback
|
||||
@@ -56,18 +56,15 @@ class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
ds_docker_conf = DSDockerConf()
|
||||
ds_docker_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"
|
||||
}
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
fname = "test/model_test.py"
|
||||
test_code = (
|
||||
(DIRNAME / "eval_tests" / "model_test.txt").read_text().replace("model01", target_task.name)
|
||||
) # only check the model changed this time
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
stdout = implementation.execute(env=de, entry=f"python {fname}")
|
||||
stdout = implementation.execute(env=env, entry=f"python {fname}")
|
||||
|
||||
if stdout is None:
|
||||
raise CoderError(
|
||||
@@ -75,7 +72,7 @@ class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
)
|
||||
|
||||
if "main.py" in implementation.file_dict:
|
||||
workflow_stdout = implementation.execute(env=de, entry="python main.py")
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import pickle
|
||||
import site
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
from rdagent.core.experiment import Experiment, FBWorkspace
|
||||
from rdagent.core.utils import cache_with_pickle
|
||||
from rdagent.oai.llm_utils import md5_hash
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
|
||||
@@ -38,7 +38,10 @@ from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import DSCoderCoSTEERSettings
|
||||
from rdagent.components.coder.data_science.conf import (
|
||||
DSCoderCoSTEERSettings,
|
||||
get_ds_env,
|
||||
)
|
||||
from rdagent.components.coder.data_science.raw_data_loader.eval import (
|
||||
DataLoaderCoSTEEREvaluator,
|
||||
)
|
||||
@@ -48,7 +51,6 @@ from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
|
||||
class DataLoaderMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
@@ -214,10 +216,10 @@ class DataLoaderCoSTEER(CoSTEER):
|
||||
def develop(self, exp):
|
||||
new_exp = super().develop(exp)
|
||||
|
||||
ds_docker_conf = DSDockerConf()
|
||||
ds_docker_conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": "/kaggle/input"}
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
stdout = new_exp.experiment_workspace.execute(env=de, entry=f"python test/data_loader_test.py")
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
stdout = new_exp.experiment_workspace.execute(env=env, entry=f"python test/data_loader_test.py")
|
||||
match = re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL)
|
||||
eda_output = match.groups()[1] if match else None
|
||||
self.scen.eda_output = eda_output
|
||||
|
||||
@@ -12,11 +12,10 @@ from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledgeV2,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
@@ -48,17 +47,14 @@ class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
ds_docker_conf = DSDockerConf()
|
||||
ds_docker_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"
|
||||
}
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
# TODO: do we need to clean the generated temporary content?
|
||||
fname = "test/data_loader_test.py"
|
||||
test_code = (DIRNAME / "eval_tests" / "data_loader_test.txt").read_text()
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
stdout = implementation.execute(env=de, entry=f"python {fname}")
|
||||
stdout = implementation.execute(env=env, entry=f"python {fname}")
|
||||
match = re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===(.*)", stdout, re.DOTALL)
|
||||
stdout_part_1, eda_output, stdout_part_2 = match.groups() if match else (stdout, None, "")
|
||||
stdout = stdout_part_1 + stdout_part_2
|
||||
@@ -66,7 +62,7 @@ class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
eda_output += "Length of EDA output is too long, truncated. Please reject this implementation and motivate it to reduce the length of EDA output."
|
||||
|
||||
if "main.py" in implementation.file_dict:
|
||||
workflow_stdout = implementation.execute(env=de, entry="python main.py")
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
import pickle
|
||||
import site
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
from rdagent.core.experiment import Experiment, FBWorkspace
|
||||
from rdagent.core.utils import cache_with_pickle
|
||||
from rdagent.oai.llm_utils import md5_hash
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
|
||||
@@ -10,12 +10,11 @@ from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiFeedback,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf, MLEBDockerConf
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
@@ -54,12 +53,8 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
# DockerEnv for Kaggle Competition
|
||||
ds_docker_conf = DSDockerConf()
|
||||
ds_docker_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"
|
||||
}
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
# # DockerEnv for MLEBench submission validation
|
||||
# mle_de_conf = MLEBDockerConf()
|
||||
@@ -70,9 +65,9 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
# mde.prepare()
|
||||
|
||||
# Clean the scores.csv & submission.csv.
|
||||
implementation.execute(env=de, entry=f"rm submission.csv scores.csv")
|
||||
implementation.execute(env=env, entry=f"rm submission.csv scores.csv")
|
||||
|
||||
stdout = implementation.execute(env=de, entry=f"python main.py")
|
||||
stdout = implementation.execute(env=env, entry=f"python main.py")
|
||||
stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", stdout)
|
||||
|
||||
# Check score file
|
||||
@@ -102,7 +97,7 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
implementation.inject_files(**{"test/submission_format_test.py": base_check_code})
|
||||
# stdout += "----Submission Check 1-----\n"
|
||||
submission_check_out, submission_ret_code = implementation.execute_ret_code(
|
||||
env=de, entry="python test/submission_format_test.py"
|
||||
env=env, entry="python test/submission_format_test.py"
|
||||
)
|
||||
stdout += "\n" + submission_check_out
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import fire
|
||||
import pandas as pd
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.proposal import ExperimentFeedback
|
||||
from rdagent.log.storage import FileStorage
|
||||
@@ -14,11 +15,8 @@ from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
|
||||
from rdagent.scenarios.kaggle.kaggle_crawler import score_rank
|
||||
from rdagent.utils.env import DockerEnv, MLEBDockerConf
|
||||
|
||||
mle_de_conf = MLEBDockerConf()
|
||||
mle_de_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data",
|
||||
}
|
||||
de = DockerEnv(conf=mle_de_conf)
|
||||
de = get_ds_env("mlebench")
|
||||
de.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data"}
|
||||
de.prepare()
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.exception import RunnerError
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
@@ -115,11 +116,8 @@ class DSCoSTEERRunner(CoSTEER):
|
||||
exp.result = pd.read_csv(score_fp, index_col=0)
|
||||
|
||||
# DockerEnv for MLEBench submission validation
|
||||
mle_de_conf = MLEBDockerConf()
|
||||
mle_de_conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data",
|
||||
}
|
||||
mde = DockerEnv(conf=mle_de_conf)
|
||||
mde = get_ds_env(conf_type="mlebench")
|
||||
mde.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data"}
|
||||
mde.prepare()
|
||||
# MLEBench Check
|
||||
mle_check_code = (
|
||||
|
||||
@@ -8,13 +8,17 @@ from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import (
|
||||
DSCoderCoSTEERSettings,
|
||||
get_ds_env,
|
||||
)
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf, MLEBDockerConf
|
||||
from rdagent.utils.env import DockerEnv, MLEBDockerConf
|
||||
from rdagent.utils.fmt import shrink_text
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
@@ -32,18 +36,16 @@ class DSCoSTEERCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
queried_knowledge: QueriedKnowledge = None,
|
||||
**kwargs,
|
||||
) -> DSCoSTEEREvalFeedback:
|
||||
ds_docker_conf = DSDockerConf()
|
||||
ds_docker_conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": "/kaggle/input"}
|
||||
ds_docker_conf.running_timeout_period = DS_RD_SETTING.full_timeout
|
||||
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": "/kaggle/input"}
|
||||
|
||||
stdout = implementation.execute(
|
||||
env=de, entry=f"rm submission.csv scores.csv"
|
||||
env=env, entry=f"rm submission.csv scores.csv"
|
||||
) # Remove previous submission and scores files generated by worklfow.
|
||||
|
||||
# execute workflow
|
||||
stdout = implementation.execute(env=de, entry="coverage run main.py")
|
||||
stdout = implementation.execute(env=env, entry="coverage run main.py")
|
||||
stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", stdout)
|
||||
|
||||
score_fp = implementation.workspace_path / "scores.csv"
|
||||
@@ -57,11 +59,10 @@ class DSCoSTEERCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
stdout += "\n Submission file (submission.csv) is not generated!"
|
||||
else:
|
||||
# DockerEnv for MLEBench submission validation
|
||||
mle_de_conf = MLEBDockerConf()
|
||||
mle_de_conf.extra_volumes = {
|
||||
mde = get_ds_env("mlebench")
|
||||
mde.conf.extra_volumes = {
|
||||
f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data",
|
||||
}
|
||||
mde = DockerEnv(conf=mle_de_conf)
|
||||
mde.prepare()
|
||||
# MLEBench Check
|
||||
mle_check_code = (
|
||||
@@ -88,7 +89,7 @@ class DSCoSTEERCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
|
||||
if feedback:
|
||||
# remove unused files
|
||||
implementation.execute(env=de, entry="coverage json -o coverage.json")
|
||||
implementation.execute(env=env, entry="coverage json -o coverage.json")
|
||||
if Path(implementation.workspace_path / "coverage.json").exists():
|
||||
with open(implementation.workspace_path / "coverage.json") as f:
|
||||
used_files = set(json.load(f)["files"].keys())
|
||||
|
||||
@@ -7,6 +7,7 @@ import pandas as pd
|
||||
from PIL import Image, TiffTags
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
@@ -16,7 +17,6 @@ from rdagent.scenarios.kaggle.kaggle_crawler import (
|
||||
leaderboard_scores,
|
||||
)
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.env import DockerEnv, DSDockerConf
|
||||
|
||||
|
||||
def read_csv_head(file_path, indent=0, lines=5, max_col_width=100):
|
||||
@@ -314,14 +314,13 @@ class DataScienceScen(Scenario):
|
||||
|
||||
def get_runtime_environment(self) -> str:
|
||||
# TODO: add it into base class. Environment should(i.e. `DSDockerConf`) should be part of the scenario class.
|
||||
ds_docker_conf = DSDockerConf()
|
||||
de = DockerEnv(conf=ds_docker_conf)
|
||||
env = get_ds_env()
|
||||
implementation = FBWorkspace()
|
||||
fname = "temp.py"
|
||||
implementation.inject_files(
|
||||
**{fname: (Path(__file__).absolute().resolve().parent / "runtime_info.py").read_text()}
|
||||
)
|
||||
stdout = implementation.execute(env=de, entry=f"python {fname}")
|
||||
stdout = implementation.execute(env=env, entry=f"python {fname}")
|
||||
return stdout
|
||||
|
||||
def _get_data_folder_description(self) -> str:
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Use the official PyTorch image as the base image
|
||||
FROM pytorch/pytorch:latest
|
||||
# FROM pytorch/pytorch:2.4.1-cuda12.1-cudnn9-runtime
|
||||
# torch.__version__ == '2.4.1+cu121' in "gcr.io/kaggle-gpu-images/python"
|
||||
|
||||
# Install additional tools
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
vim \
|
||||
git \
|
||||
build-essential \
|
||||
git-lfs \
|
||||
unzip && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Default command to keep the container running
|
||||
ENTRYPOINT ["/workspace/run/entrypoint.sh"]
|
||||
|
||||
RUN conda init bash
|
||||
|
||||
# MLE-Bench
|
||||
RUN conda create -n mlebench python==3.11 pip -y
|
||||
RUN cd /workspace && git clone https://github.com/openai/mle-bench.git
|
||||
RUN cd /workspace/mle-bench && git lfs fetch --all
|
||||
RUN cd /workspace/mle-bench && git lfs pull
|
||||
RUN cd /workspace/mle-bench && conda run -n mlebench pip install -e .
|
||||
|
||||
# Kaggle Environment
|
||||
COPY ./kaggle_environment.yaml /workspace
|
||||
RUN cd /workspace && conda env create -f /workspace/kaggle_environment.yaml
|
||||
|
||||
# RD-Agent
|
||||
RUN cd /workspace && git clone https://github.com/microsoft/RD-Agent
|
||||
RUN cd RD-Agent && git fetch && make dev
|
||||
|
||||
|
||||
# litellm
|
||||
RUN cd /workspace && mkdir -p litellm-srv
|
||||
RUN cd /workspace/litellm-srv && curl https://raw.githubusercontent.com/you-n-g/deploy/refs/heads/master/configs/python/litellm.trapi.yaml -o litellm.trapi.yaml
|
||||
RUN pip install 'litellm[proxy]'
|
||||
RUN pip install git+https://github.com/you-n-g/litellm@add_mi_cred_pr
|
||||
|
||||
run cd /workspace && mkdir -p run
|
||||
COPY ./entrypoint.sh /workspace/run
|
||||
|
||||
|
||||
WORKDIR /workspace/RD-Agent/
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
|
||||
DIR="$( cd "$(dirname "$(readlink -f "$0")")" || exit ; pwd -P )"
|
||||
|
||||
sudo mkdir -p /mle/ /kaggle/
|
||||
|
||||
CURRENT_USER=$(id -un)
|
||||
sudo chown -R $CURRENT_USER:$CURRENT_USER /workspace/ /mle/ /kaggle/
|
||||
|
||||
ls -lat /
|
||||
|
||||
cd $DIR/../RD-Agent
|
||||
mkdir -p log/
|
||||
git fetch
|
||||
git checkout ${RD_COMMIT:-ee8d97c52062607cac778b8aeb10769b075a8d11}
|
||||
make dev
|
||||
pip install 'litellm[proxy]'
|
||||
pip install git+https://github.com/you-n-g/litellm@add_mi_cred_pr
|
||||
|
||||
|
||||
cd $DIR/../litellm-srv/
|
||||
export AZURE_CLIENT_ID
|
||||
export AZURE_SCOPE=api://trapi/.default
|
||||
export AZURE_CREDENTIAL=ManagedIdentityCredential
|
||||
sed -i '/proxy_handler_instance/d' litellm.trapi.yaml # remove useless handler in production
|
||||
nohup litellm --config litellm.trapi.yaml &
|
||||
|
||||
sleep 10 # wait for litellm to start
|
||||
|
||||
|
||||
cd $DIR/../RD-Agent
|
||||
script -c "timeout ${RD_TIMEOUT:-24h} python rdagent/app/data_science/loop.py --competition $DS_COMPETITION" log/stdout.${DS_COMPETITION}.log
|
||||
|
||||
unset LOG_TRACE_PATH # avoid make the original log dirty.
|
||||
python rdagent/log/mle_summary.py grade_summary --log_folder=./log/
|
||||
|
||||
tar cf log.tar log
|
||||
|
||||
# NOTE: when we have $AMLT_OUTPUT_DIR, maybe we don't have to copy file actively to azure blob now.
|
||||
# RD_OUTPUT_DIR=${RD_OUTPUT_DIR:-/data/rdagent}/
|
||||
# mkdir -p $RD_OUTPUT_DIR
|
||||
# cp -r log.tar $RD_OUTPUT_DIR/${RD_RES_NAME:-log.tar}
|
||||
|
||||
cp -r log.tar $AMLT_OUTPUT_DIR/${RD_RES_NAME:-log.tar}
|
||||
|
||||
set > $AMLT_OUTPUT_DIR/env
|
||||
@@ -0,0 +1,367 @@
|
||||
name: kaggle
|
||||
channels:
|
||||
- defaults
|
||||
- https://repo.anaconda.com/pkgs/main
|
||||
- https://repo.anaconda.com/pkgs/r
|
||||
dependencies:
|
||||
- _libgcc_mutex=0.1=main
|
||||
- _openmp_mutex=5.1=1_gnu
|
||||
- bzip2=1.0.8=h5eee18b_6
|
||||
- ca-certificates=2025.2.25=h06a4308_0
|
||||
- ld_impl_linux-64=2.40=h12ee557_0
|
||||
- libffi=3.4.4=h6a678d5_1
|
||||
- libgcc-ng=11.2.0=h1234567_1
|
||||
- libgomp=11.2.0=h1234567_1
|
||||
- libstdcxx-ng=11.2.0=h1234567_1
|
||||
- libuuid=1.41.5=h5eee18b_0
|
||||
- ncurses=6.4=h6a678d5_0
|
||||
- openssl=3.0.15=h5eee18b_0
|
||||
- pip=25.0=py311h06a4308_0
|
||||
- python=3.11.11=he870216_0
|
||||
- readline=8.2=h5eee18b_0
|
||||
- setuptools=75.8.0=py311h06a4308_0
|
||||
- sqlite=3.45.3=h5eee18b_0
|
||||
- tk=8.6.14=h39e8969_0
|
||||
- wheel=0.45.1=py311h06a4308_0
|
||||
- xz=5.6.4=h5eee18b_1
|
||||
- zlib=1.2.13=h5eee18b_1
|
||||
- pip:
|
||||
- absl-py==2.1.0
|
||||
- accelerate==0.33.0
|
||||
- aideml==0.1.4
|
||||
- aiohappyeyeballs==2.4.6
|
||||
- aiohttp==3.11.13
|
||||
- aiosignal==1.3.2
|
||||
- albucore==0.0.23
|
||||
- albumentations==1.4.14
|
||||
- alembic==1.14.1
|
||||
- annotated-types==0.7.0
|
||||
- anthropic==0.34.1
|
||||
- antlr4-python3-runtime==4.9.3
|
||||
- anyio==4.8.0
|
||||
- arrow==1.3.0
|
||||
- asttokens==3.0.0
|
||||
- astunparse==1.6.3
|
||||
- attrs==25.1.0
|
||||
- audioread==3.0.1
|
||||
- azure-ai-formrecognizer==3.3.3
|
||||
- azure-common==1.1.28
|
||||
- azure-core==1.32.0
|
||||
- azure-identity==1.20.0
|
||||
- azure-storage-blob==12.24.1
|
||||
- backoff==2.2.1
|
||||
- bayesian-optimization==1.5.1
|
||||
- bayespy==0.5.1
|
||||
- biopython==1.84
|
||||
- black==24.3.0
|
||||
- bleach==6.2.0
|
||||
- blis==0.7.11
|
||||
- brotli==1.1.0
|
||||
- bson==0.5.10
|
||||
- cachetools==5.5.2
|
||||
- catalogue==2.0.10
|
||||
- catboost==1.2.5
|
||||
- certifi==2025.1.31
|
||||
- cffi==1.17.1
|
||||
- charset-normalizer==3.4.1
|
||||
- click==8.1.8
|
||||
- cloudpathlib==0.20.0
|
||||
- cloudpickle==3.1.1
|
||||
- colorama==0.4.6
|
||||
- colorlog==6.9.0
|
||||
- comm==0.2.2
|
||||
- confection==0.1.5
|
||||
- contourpy==1.3.1
|
||||
- coolname==2.2.0
|
||||
- cryptography==44.0.2
|
||||
- cycler==0.12.1
|
||||
- cymem==2.0.11
|
||||
- cython==3.0.11
|
||||
- dacite==1.8.1
|
||||
- dataclasses-json==0.6.7
|
||||
- datasets==2.1.0
|
||||
- debugpy==1.8.12
|
||||
- decorator==5.2.1
|
||||
- defusedxml==0.7.1
|
||||
- dill==0.3.9
|
||||
- distro==1.9.0
|
||||
- efficientnet-pytorch==0.7.1
|
||||
- eval-type-backport==0.2.2
|
||||
- evaluate==0.4.2
|
||||
- executing==2.2.0
|
||||
- fastai==2.7.17
|
||||
- fastcore==1.7.29
|
||||
- fastdownload==0.0.7
|
||||
- fastdtw==0.3.4
|
||||
- fastjsonschema==2.21.1
|
||||
- fastprogress==1.0.3
|
||||
- faust-cchardet==2.1.19
|
||||
- filelock==3.17.0
|
||||
- flatbuffers==25.2.10
|
||||
- fonttools==4.56.0
|
||||
- frozenlist==1.5.0
|
||||
- fsspec==2025.2.0
|
||||
- funcy==2.0
|
||||
- future==1.0.0
|
||||
- gast==0.6.0
|
||||
- gdcm==1.1
|
||||
- gensim==4.3.3
|
||||
- genson==1.3.0
|
||||
- geographiclib==2.0
|
||||
- geopy==2.4.1
|
||||
- graphviz==0.20.3
|
||||
- greenlet==3.1.1
|
||||
- grpcio==1.71.0rc2
|
||||
- gym==0.26.2
|
||||
- gym-notices==0.0.8
|
||||
- h11==0.14.0
|
||||
- h5py==3.11.0
|
||||
- hmmlearn==0.3.2
|
||||
- httpcore==1.0.7
|
||||
- httplib2==0.22.0
|
||||
- httpx==0.27.2
|
||||
- huggingface-hub==0.29.1
|
||||
- humanize==4.8.0
|
||||
- hyperopt==0.2.7
|
||||
- idna==3.10
|
||||
- igraph==0.11.6
|
||||
- imagecodecs==2024.6.1
|
||||
- imageio==2.37.0
|
||||
- imbalanced-learn==0.12.3
|
||||
- imgaug==0.4.0
|
||||
- implicit==0.7.2
|
||||
- inflate64==1.0.1
|
||||
- iniconfig==2.0.0
|
||||
- ipykernel==6.29.5
|
||||
- ipython==8.27.0
|
||||
- isodate==0.7.2
|
||||
- jedi==0.19.2
|
||||
- jinja2==3.1.5
|
||||
- jiter==0.8.2
|
||||
- joblib==1.4.2
|
||||
- jsonlines==4.0.0
|
||||
- jsonpatch==1.33
|
||||
- jsonpointer==3.0.0
|
||||
- jsonschema==4.19.2
|
||||
- jsonschema-specifications==2024.10.1
|
||||
- jupyter-client==8.6.3
|
||||
- jupyter-core==5.7.2
|
||||
- kaggle==1.6.17
|
||||
- keras==3.5.0
|
||||
- kiwisolver==1.4.8
|
||||
- kornia==0.6.10
|
||||
- kornia-rs==0.1.8
|
||||
- langchain==0.2.15
|
||||
- langchain-anthropic==0.1.23
|
||||
- langchain-core==0.2.43
|
||||
- langchain-text-splitters==0.2.4
|
||||
- langcodes==3.5.0
|
||||
- langsmith==0.1.147
|
||||
- language-data==1.3.0
|
||||
- lazy-loader==0.4
|
||||
- levenshtein==0.25.1
|
||||
- libclang==18.1.1
|
||||
- librosa==0.10.2.post1
|
||||
- lightgbm==4.5.0
|
||||
- lightning-utilities==0.12.0
|
||||
- littleutils==0.2.4
|
||||
- llvmlite==0.43.0
|
||||
- loguru==0.7.2
|
||||
- lxml==5.3.1
|
||||
- mako==1.3.9
|
||||
- marisa-trie==1.2.1
|
||||
- markdown==3.7
|
||||
- markdown-it-py==3.0.0
|
||||
- markovify==0.9.4
|
||||
- markupsafe==3.0.2
|
||||
- marshmallow==3.26.1
|
||||
- matplotlib==3.9.2
|
||||
- matplotlib-inline==0.1.7
|
||||
- mdurl==0.1.2
|
||||
- ml-dtypes==0.4.1
|
||||
- mpmath==1.3.0
|
||||
- msal==1.31.1
|
||||
- msal-extensions==1.2.0
|
||||
- msgpack==1.1.0
|
||||
- msgpack-numpy==0.4.8
|
||||
- msrest==0.7.1
|
||||
- multidict==6.1.0
|
||||
- multiprocess==0.70.17
|
||||
- multivolumefile==0.2.3
|
||||
- munch==4.0.0
|
||||
- murmurhash==1.0.12
|
||||
- mypy-extensions==1.0.0
|
||||
- namex==0.0.8
|
||||
- nbformat==5.10.4
|
||||
- nest-asyncio==1.6.0
|
||||
- networkx==3.3
|
||||
- nltk==3.9.1
|
||||
- numba==0.60.0
|
||||
- numpy==1.26.2
|
||||
- nvidia-cublas-cu12==12.1.3.1
|
||||
- nvidia-cuda-cupti-cu12==12.1.105
|
||||
- nvidia-cuda-nvcc-cu12==12.3.107
|
||||
- nvidia-cuda-nvrtc-cu12==12.1.105
|
||||
- nvidia-cuda-runtime-cu12==12.1.105
|
||||
- nvidia-cudnn-cu12==8.9.2.26
|
||||
- nvidia-cufft-cu12==11.0.2.54
|
||||
- nvidia-curand-cu12==10.3.2.106
|
||||
- nvidia-cusolver-cu12==11.4.5.107
|
||||
- nvidia-cusparse-cu12==12.1.0.106
|
||||
- nvidia-nccl-cu12==2.19.3
|
||||
- nvidia-nvjitlink-cu12==12.3.101
|
||||
- nvidia-nvtx-cu12==12.1.105
|
||||
- oauthlib==3.2.2
|
||||
- ogb==1.3.6
|
||||
- omegaconf==2.3.0
|
||||
- openai==1.48.0
|
||||
- opencv-python==4.10.0.84
|
||||
- opencv-python-headless==4.11.0.86
|
||||
- opt-einsum==3.4.0
|
||||
- optree==0.14.1
|
||||
- optuna==4.0.0
|
||||
- orjson==3.10.15
|
||||
- outdated==0.2.2
|
||||
- packaging==24.2
|
||||
- pandas==2.1.4
|
||||
- parso==0.8.4
|
||||
- pathspec==0.12.1
|
||||
- pdf2image==1.17.0
|
||||
- peft==0.12.0
|
||||
- pexpect==4.9.0
|
||||
- pillow==10.4.0
|
||||
- platformdirs==4.3.6
|
||||
- plotly==5.24.0
|
||||
- pluggy==1.5.0
|
||||
- pooch==1.8.2
|
||||
- portalocker==2.10.1
|
||||
- preshed==3.0.9
|
||||
- pretrainedmodels==0.7.4
|
||||
- prompt-toolkit==3.0.50
|
||||
- propcache==0.3.0
|
||||
- proto-plus==1.26.0
|
||||
- psutil==7.0.0
|
||||
- ptyprocess==0.7.0
|
||||
- pure-eval==0.2.3
|
||||
- py4j==0.10.9.9
|
||||
- py7zr==0.22.0
|
||||
- pyaml==25.1.0
|
||||
- pyarrow==17.0.0
|
||||
- pyasn1==0.6.1
|
||||
- pyasn1-modules==0.4.1
|
||||
- pybcj==1.0.3
|
||||
- pycparser==2.22
|
||||
- pycryptodomex==3.21.0
|
||||
- pydantic==2.9.2
|
||||
- pydantic-core==2.23.4
|
||||
- pydantic-settings==2.6.1
|
||||
- pydicom==2.4.4
|
||||
- pygments==2.19.1
|
||||
- pyjwt==2.10.1
|
||||
- pylibjpeg==2.0.1
|
||||
- pyocr==0.8.5
|
||||
- pyparsing==3.1.4
|
||||
- pypdf==4.3.1
|
||||
- pyppmd==1.1.1
|
||||
- pytest==7.4.3
|
||||
- python-dateutil==2.9.0.post0
|
||||
- python-dotenv==1.0.1
|
||||
- python-slugify==8.0.4
|
||||
- pytorch-lightning==2.4.0
|
||||
- pytz==2024.1
|
||||
- pyyaml==6.0.2
|
||||
- pyzmq==26.2.1
|
||||
- pyzstd==0.16.2
|
||||
- ranger21==0.1.0
|
||||
- rapidfuzz==3.12.2
|
||||
- referencing==0.36.2
|
||||
- regex==2024.11.6
|
||||
- requests==2.31.0
|
||||
- requests-oauthlib==2.0.0
|
||||
- requests-toolbelt==1.0.0
|
||||
- resampy==0.4.3
|
||||
- responses==0.18.0
|
||||
- rich==13.7.0
|
||||
- rouge-score==0.1.2
|
||||
- rpds-py==0.23.1
|
||||
- rsa==4.9
|
||||
- sacrebleu==2.4.3
|
||||
- safetensors==0.5.3
|
||||
- scikit-image==0.24.0
|
||||
- scikit-learn==1.2.2
|
||||
- scikit-optimize==0.10.2
|
||||
- scikit-surprise==1.1.4
|
||||
- scipy==1.11.4
|
||||
- seaborn==0.13.2
|
||||
- segmentation-models-pytorch==0.3.4
|
||||
- sentence-transformers==3.0.1
|
||||
- sentencepiece==0.2.0
|
||||
- shapely==2.0.7
|
||||
- shellingham==1.5.4
|
||||
- shutup==0.2.0
|
||||
- simsimd==6.2.1
|
||||
- six==1.17.0
|
||||
- sklearn-pandas==2.2.0
|
||||
- smart-open==7.1.0
|
||||
- sniffio==1.3.1
|
||||
- soundfile==0.13.1
|
||||
- soxr==0.5.0.post1
|
||||
- spacy==3.7.6
|
||||
- spacy-legacy==3.0.12
|
||||
- spacy-loggers==1.0.5
|
||||
- sqlalchemy==2.0.38
|
||||
- srsly==2.5.1
|
||||
- stack-data==0.6.3
|
||||
- stringzilla==3.12.2
|
||||
- sympy==1.13.2
|
||||
- tabulate==0.9.0
|
||||
- tenacity==8.5.0
|
||||
- tensorboard==2.17.1
|
||||
- tensorboard-data-server==0.7.2
|
||||
- tensorflow==2.17.0
|
||||
- tensorflow-hub==0.16.1
|
||||
- tensorflow-io-gcs-filesystem==0.37.1
|
||||
- tensorpack==0.11
|
||||
- termcolor==2.5.0
|
||||
- text-unidecode==1.3
|
||||
- textblob==0.18.0.post0
|
||||
- texttable==1.7.0
|
||||
- tf-keras==2.17.0
|
||||
- thinc==8.2.5
|
||||
- threadpoolctl==3.5.0
|
||||
- tifffile==2025.2.18
|
||||
- tiktoken==0.7.0
|
||||
- timm==0.9.7
|
||||
- tokenizers==0.19.1
|
||||
- torch==2.2.0
|
||||
- torch-geometric==2.3.1
|
||||
- torchaudio==2.2.0
|
||||
- torchdata==0.7.1
|
||||
- torchinfo==1.8.0
|
||||
- torchmetrics==1.3.1
|
||||
- torchtext==0.17.0
|
||||
- torchvision==0.17.0
|
||||
- tornado==6.4.2
|
||||
- tqdm==4.66.2
|
||||
- traitlets==5.14.3
|
||||
- transformers==4.44.2
|
||||
- triton==2.2.0
|
||||
- typer==0.15.2
|
||||
- types-python-dateutil==2.9.0.20241206
|
||||
- typing-extensions==4.12.2
|
||||
- typing-inspect==0.9.0
|
||||
- tzdata==2025.1
|
||||
- unidecode==1.3.8
|
||||
- uritemplate==4.1.1
|
||||
- urllib3==2.3.0
|
||||
- wasabi==1.1.3
|
||||
- wcwidth==0.2.13
|
||||
- weasel==0.4.1
|
||||
- webencodings==0.5.1
|
||||
- werkzeug==3.1.3
|
||||
- wrapt==1.17.2
|
||||
- xgboost==2.1.1
|
||||
- xlrd==2.0.1
|
||||
- xxhash==3.5.0
|
||||
- yarl==1.18.3
|
||||
prefix: /opt/conda/envs/kaggle
|
||||
@@ -114,9 +114,9 @@ def download_data(competition: str, settings: ExtendedBaseSettings = KAGGLE_IMPL
|
||||
zipfile_path = f"{local_path}/zip_files"
|
||||
zip_competition_path = Path(zipfile_path) / competition
|
||||
|
||||
mleb_env = MLEBDockerEnv()
|
||||
mleb_env.prepare()
|
||||
if not zip_competition_path.exists():
|
||||
mleb_env = MLEBDockerEnv()
|
||||
mleb_env.prepare()
|
||||
(Path(zipfile_path)).mkdir(parents=True, exist_ok=True)
|
||||
mleb_env.run(
|
||||
f"mlebench prepare -c {competition} --data-dir ./zip_files",
|
||||
@@ -127,6 +127,8 @@ def download_data(competition: str, settings: ExtendedBaseSettings = KAGGLE_IMPL
|
||||
if not (Path(local_path) / competition).exists() or list((Path(local_path) / competition).iterdir()) == []:
|
||||
(Path(local_path) / competition).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mleb_env = MLEBDockerEnv()
|
||||
mleb_env.prepare()
|
||||
mleb_env.run(f"cp -r ./zip_files/{competition}/prepared/public/* ./{competition}", local_path=local_path)
|
||||
|
||||
for zip_path in (Path(local_path) / competition).rglob("*.zip"):
|
||||
|
||||
+236
-144
@@ -19,16 +19,17 @@ import zipfile
|
||||
from abc import abstractmethod
|
||||
from pathlib import Path
|
||||
from types import MappingProxyType
|
||||
from typing import Generic, Mapping, Optional, TypeVar
|
||||
from typing import Any, Generic, Mapping, Optional, TypeVar
|
||||
|
||||
import docker # type: ignore[import-untyped]
|
||||
import docker.models # type: ignore[import-untyped]
|
||||
import docker.models.containers # type: ignore[import-untyped]
|
||||
import docker.types # type: ignore[import-untyped]
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, model_validator
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
from rich import print
|
||||
from rich.console import Console
|
||||
from rich.pretty import Pretty
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn
|
||||
from rich.rule import Rule
|
||||
from rich.table import Table
|
||||
@@ -39,23 +40,53 @@ from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.oai.llm_utils import md5_hash
|
||||
from rdagent.utils.workflow import wait_retry
|
||||
|
||||
ASpecificBaseModel = TypeVar("ASpecificBaseModel", bound=BaseModel)
|
||||
|
||||
class EnvConf(BaseModel):
|
||||
default_entry: str
|
||||
extra_volumes: dict = {}
|
||||
running_timeout_period: int = 600 # 10 minutes
|
||||
# helper settings to support transparent;
|
||||
enable_cache: bool = True
|
||||
retry_count: int = 5 # retry count for the docker run
|
||||
retry_wait_seconds: int = 10 # retry wait seconds for the docker run
|
||||
|
||||
|
||||
class Env(Generic[ASpecificBaseModel]):
|
||||
ASpecificEnvConf = TypeVar("ASpecificEnvConf", bound=EnvConf)
|
||||
|
||||
|
||||
class Env(Generic[ASpecificEnvConf]):
|
||||
"""
|
||||
We use BaseModel as the setting due to the features it provides
|
||||
- It provides base typing and checking features.
|
||||
- loading and dumping the information will be easier: for example, we can use package like `pydantic-yaml`
|
||||
"""
|
||||
|
||||
conf: ASpecificBaseModel # different env have different conf.
|
||||
conf: ASpecificEnvConf # different env have different conf.
|
||||
|
||||
# last_exit_code: # TODO: get the more concrete information about the exit code.
|
||||
|
||||
def __init__(self, conf: ASpecificBaseModel):
|
||||
def __init__(self, conf: ASpecificEnvConf):
|
||||
self.conf = conf
|
||||
|
||||
def zip_a_folder_into_a_file(self, folder_path: str, zip_file_path: str) -> None:
|
||||
"""
|
||||
Zip a folder into a file, use zipfile instead of subprocess
|
||||
"""
|
||||
with zipfile.ZipFile(zip_file_path, "w") as z:
|
||||
for root, _, files in os.walk(folder_path):
|
||||
for file in files:
|
||||
z.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), folder_path))
|
||||
|
||||
def unzip_a_file_into_a_folder(self, zip_file_path: str, folder_path: str) -> None:
|
||||
"""
|
||||
Unzip a file into a folder, use zipfile instead of subprocess
|
||||
"""
|
||||
# Clear folder_path before extracting
|
||||
if os.path.exists(folder_path):
|
||||
shutil.rmtree(folder_path)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
with zipfile.ZipFile(zip_file_path, "r") as z:
|
||||
z.extractall(folder_path)
|
||||
|
||||
@abstractmethod
|
||||
def prepare(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""
|
||||
@@ -86,9 +117,35 @@ class Env(Generic[ASpecificBaseModel]):
|
||||
stdout, _ = self.run_ret_code(entry=entry, local_path=local_path, env=env, **kwargs)
|
||||
return stdout
|
||||
|
||||
@abstractmethod
|
||||
def __run_ret_code_with_retry(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
remove_timestamp: bool = True,
|
||||
) -> tuple[str, int]:
|
||||
# TODO: remove_timestamp can be implemented in a shallower way...
|
||||
for retry_index in range(self.conf.retry_count + 1):
|
||||
try:
|
||||
return self._run_ret_code(
|
||||
entry, local_path, env, running_extra_volume=running_extra_volume, remove_timestamp=remove_timestamp
|
||||
)
|
||||
except Exception as e:
|
||||
if retry_index == self.conf.retry_count:
|
||||
raise
|
||||
logger.warning(
|
||||
f"Error while running the container: {e}, current try index: {retry_index + 1}, {self.conf.retry_count - retry_index - 1} retries left."
|
||||
)
|
||||
time.sleep(self.conf.retry_wait_seconds)
|
||||
raise RuntimeError # for passing CI
|
||||
|
||||
def run_ret_code(
|
||||
self, entry: str | None, local_path: str = ".", env: dict | None = None, **kwargs: dict
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
**kwargs: dict,
|
||||
) -> tuple[str, int]:
|
||||
"""
|
||||
Run the folder under the environment and return both the stdout and the exit code.
|
||||
@@ -110,56 +167,205 @@ class Env(Generic[ASpecificBaseModel]):
|
||||
-------
|
||||
A tuple containing the stdout and the exit code
|
||||
"""
|
||||
running_extra_volume = kwargs.get("running_extra_volume", {})
|
||||
if entry is None:
|
||||
entry = self.conf.default_entry
|
||||
|
||||
entry_add_timeout = (
|
||||
f"/bin/sh -c 'timeout {self.conf.running_timeout_period} {entry}; "
|
||||
+ "entry_exit_code=$?; "
|
||||
+ (f"chmod -R 777 {self.conf.mount_path}; " if hasattr(self.conf, "mount_path") else "")
|
||||
+ "exit $entry_exit_code'"
|
||||
)
|
||||
|
||||
if self.conf.enable_cache:
|
||||
stdout, return_code = self.cached_run(entry_add_timeout, local_path, env, running_extra_volume)
|
||||
else:
|
||||
stdout, return_code = self.__run_ret_code_with_retry(
|
||||
entry_add_timeout, local_path, env, running_extra_volume, remove_timestamp=False
|
||||
)
|
||||
|
||||
return stdout, return_code
|
||||
|
||||
def cached_run(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
remove_timestamp: bool = True,
|
||||
) -> tuple[str, int]:
|
||||
"""
|
||||
Run the folder under the environment.
|
||||
Will cache the output and the folder diff for next round of running.
|
||||
Use the python codes and the parameters(entry, running_extra_volume) as key to hash the input.
|
||||
"""
|
||||
target_folder = Path(RD_AGENT_SETTINGS.pickle_cache_folder_path_str) / f"utils.env.run"
|
||||
target_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# we must add the information of data (beyound code) into the key.
|
||||
# Otherwise, all commands operating on data will become invalue (e.g. rm -r submission.csv)
|
||||
# So we recursively walk in the folder and add the sorted relative filename list as part of the key.
|
||||
data_key = []
|
||||
for path in Path(local_path).rglob("*"):
|
||||
p = str(path.relative_to(Path(local_path)))
|
||||
if p.startswith("__pycache__"):
|
||||
continue
|
||||
data_key.append(p)
|
||||
data_key = sorted(data_key)
|
||||
|
||||
key = md5_hash(
|
||||
json.dumps(
|
||||
[
|
||||
[str(path.relative_to(Path(local_path))), path.read_text()]
|
||||
for path in sorted(Path(local_path).rglob("*.py"))
|
||||
]
|
||||
)
|
||||
+ json.dumps({"entry": entry, "running_extra_volume": dict(running_extra_volume)})
|
||||
+ json.dumps({"extra_volumes": self.conf.extra_volumes})
|
||||
+ json.dumps(data_key)
|
||||
)
|
||||
if Path(target_folder / f"{key}.pkl").exists() and Path(target_folder / f"{key}.zip").exists():
|
||||
with open(target_folder / f"{key}.pkl", "rb") as f:
|
||||
ret: tuple[str, int] = pickle.load(f)
|
||||
self.unzip_a_file_into_a_folder(str(target_folder / f"{key}.zip"), local_path)
|
||||
else:
|
||||
ret = self.__run_ret_code_with_retry(entry, local_path, env, running_extra_volume, remove_timestamp)
|
||||
with open(target_folder / f"{key}.pkl", "wb") as f:
|
||||
pickle.dump(ret, f)
|
||||
self.zip_a_folder_into_a_file(local_path, str(target_folder / f"{key}.zip"))
|
||||
return ret
|
||||
|
||||
@abstractmethod
|
||||
def _run_ret_code(
|
||||
self,
|
||||
entry: str | None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
**kwargs: Any,
|
||||
) -> tuple[str, int]:
|
||||
"""
|
||||
Execute the specified entry point within the given environment and local path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
entry : str | None
|
||||
The entry point to execute. If None, defaults to the configured entry.
|
||||
local_path : str
|
||||
The local directory path where the execution should occur.
|
||||
env : dict | None
|
||||
Environment variables to set during execution.
|
||||
kwargs : dict
|
||||
Additional keyword arguments for execution customization.
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[str, int]
|
||||
A tuple containing the standard output and the exit code of the execution.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# class EnvWithCache
|
||||
#
|
||||
|
||||
## Local Environment -----
|
||||
|
||||
|
||||
class LocalConf(BaseModel):
|
||||
py_bin: str
|
||||
default_entry: str
|
||||
class LocalConf(EnvConf):
|
||||
bin_path: str = ""
|
||||
"""path like <path1>:<path2>:<path3>, which will be prepend to bin path."""
|
||||
|
||||
retry_count: int = 0 # retry count for; run `retry_count + 1` times
|
||||
|
||||
|
||||
class LocalEnv(Env[LocalConf]):
|
||||
ASpecificLocalConf = TypeVar("ASpecificLocalConf", bound=LocalConf)
|
||||
|
||||
|
||||
class LocalEnv(Env[ASpecificLocalConf]):
|
||||
"""
|
||||
Sometimes local environment may be more convinient for testing
|
||||
"""
|
||||
|
||||
def prepare(self) -> None:
|
||||
if not (Path("~/.qlib/qlib_data/cn_data").expanduser().resolve().exists()):
|
||||
self.run(
|
||||
entry="python -m qlib.run.get_data qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn",
|
||||
)
|
||||
else:
|
||||
print("Data already exists. Download skipped.")
|
||||
def prepare(self) -> None: ...
|
||||
|
||||
def run_ret_code(
|
||||
def _run_ret_code(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str | None = None,
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
**kwargs: dict,
|
||||
) -> tuple[str, int]:
|
||||
|
||||
# mocking the volumns
|
||||
volumns = {}
|
||||
if self.conf.extra_volumes is not None:
|
||||
for lp, rp in self.conf.extra_volumes.items():
|
||||
volumns[lp] = rp
|
||||
for lp, rp in running_extra_volume.items():
|
||||
volumns[lp] = rp
|
||||
|
||||
for rp, lp in volumns.items():
|
||||
link_path = Path(lp)
|
||||
real_path = Path(rp)
|
||||
if not link_path.parent.exists():
|
||||
link_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if link_path.exists() or link_path.is_symlink():
|
||||
link_path.unlink()
|
||||
link_path.symlink_to(real_path)
|
||||
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
path = [*self.conf.bin_path.split(":"), "/bin/", "/usr/bin/", *env.get("PATH", "").split(":")]
|
||||
env["PATH"] = ":".join(path)
|
||||
|
||||
if entry is None:
|
||||
entry = self.conf.default_entry
|
||||
|
||||
command = str(Path(self.conf.py_bin).joinpath(entry)).split(" ")
|
||||
summary = {
|
||||
"entry": entry,
|
||||
"local_path": local_path,
|
||||
"env": env,
|
||||
"volumes": volumns,
|
||||
}
|
||||
print(Pretty(summary))
|
||||
|
||||
cwd = None
|
||||
if local_path:
|
||||
cwd = Path(local_path).resolve()
|
||||
result = subprocess.run(command, cwd=cwd, env={**os.environ, **env}, capture_output=True, text=True)
|
||||
|
||||
return result.stdout, result.returncode
|
||||
result = subprocess.run(entry, cwd=cwd, env={**os.environ, **env}, capture_output=True, text=True, shell=True)
|
||||
combined_output = result.stderr + result.stdout # Combine stdout and stderr
|
||||
print(combined_output) # Display the combined output in the console
|
||||
|
||||
return combined_output, result.returncode
|
||||
|
||||
|
||||
class CondaConf(LocalConf):
|
||||
conda_env_name: str
|
||||
default_entry: str = "python main.py"
|
||||
|
||||
@model_validator(mode="after")
|
||||
def change_bin_path(self, **data: Any) -> "CondaConf":
|
||||
conda_path_result = subprocess.run(
|
||||
f"conda run -n {self.conda_env_name} --no-capture-output env | grep '^PATH='",
|
||||
capture_output=True,
|
||||
text=True,
|
||||
shell=True,
|
||||
)
|
||||
self.bin_path = conda_path_result.stdout.strip().split("=")[1] if conda_path_result.returncode == 0 else ""
|
||||
return self
|
||||
|
||||
|
||||
class MLECondaConf(CondaConf):
|
||||
enable_cache: bool = False # aligning with the docker settings.
|
||||
|
||||
|
||||
## Docker Environment -----
|
||||
|
||||
|
||||
class DockerConf(ExtendedBaseSettings):
|
||||
class DockerConf(EnvConf, ExtendedBaseSettings):
|
||||
build_from_dockerfile: bool = False
|
||||
dockerfile_folder_path: Optional[Path] = (
|
||||
None # the path to the dockerfile optional path provided when build_from_dockerfile is False
|
||||
@@ -369,13 +575,14 @@ class DockerEnv(Env[DockerConf]):
|
||||
output_string = re.sub(datetime_pattern, "[DATETIME]", input_string)
|
||||
return output_string
|
||||
|
||||
def __run_ret_code(
|
||||
def _run_ret_code(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
remove_timestamp: bool = True,
|
||||
**kwargs: Any,
|
||||
) -> tuple[str, int]:
|
||||
if env is None:
|
||||
env = {}
|
||||
@@ -446,121 +653,6 @@ class DockerEnv(Env[DockerConf]):
|
||||
except docker.errors.APIError as e:
|
||||
raise RuntimeError(f"Error while running the container: {e}")
|
||||
|
||||
def __run_ret_code_with_retry(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
remove_timestamp: bool = True,
|
||||
) -> tuple[str, int]:
|
||||
for retry_index in range(self.conf.retry_count):
|
||||
try:
|
||||
return self.__run_ret_code(entry, local_path, env, running_extra_volume, remove_timestamp)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Error while running the container: {e}, current try index: {retry_index + 1}, {self.conf.retry_count - retry_index - 1} retries left."
|
||||
)
|
||||
time.sleep(self.conf.retry_wait_seconds)
|
||||
raise RuntimeError("Error while running the container. Retry count exceeded.")
|
||||
|
||||
def zip_a_folder_into_a_file(self, folder_path: str, zip_file_path: str) -> None:
|
||||
"""
|
||||
Zip a folder into a file, use zipfile instead of subprocess
|
||||
"""
|
||||
with zipfile.ZipFile(zip_file_path, "w") as z:
|
||||
for root, _, files in os.walk(folder_path):
|
||||
for file in files:
|
||||
z.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), folder_path))
|
||||
|
||||
def unzip_a_file_into_a_folder(self, zip_file_path: str, folder_path: str) -> None:
|
||||
"""
|
||||
Unzip a file into a folder, use zipfile instead of subprocess
|
||||
"""
|
||||
# Clear folder_path before extracting
|
||||
if os.path.exists(folder_path):
|
||||
shutil.rmtree(folder_path)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
with zipfile.ZipFile(zip_file_path, "r") as z:
|
||||
z.extractall(folder_path)
|
||||
|
||||
def cached_run(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
running_extra_volume: Mapping = MappingProxyType({}),
|
||||
remove_timestamp: bool = True,
|
||||
) -> tuple[str, int]:
|
||||
"""
|
||||
Run the folder under the environment.
|
||||
Will cache the output and the folder diff for next round of running.
|
||||
Use the python codes and the parameters(entry, running_extra_volume) as key to hash the input.
|
||||
"""
|
||||
target_folder = Path(RD_AGENT_SETTINGS.pickle_cache_folder_path_str) / f"utils.env.run"
|
||||
target_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# we must add the information of data (beyound code) into the key.
|
||||
# Otherwise, all commands operating on data will become invalue (e.g. rm -r submission.csv)
|
||||
# So we recursively walk in the folder and add the sorted relative filename list as part of the key.
|
||||
data_key = []
|
||||
for path in Path(local_path).rglob("*"):
|
||||
p = str(path.relative_to(Path(local_path)))
|
||||
if p.startswith("__pycache__"):
|
||||
continue
|
||||
data_key.append(p)
|
||||
data_key = sorted(data_key)
|
||||
|
||||
key = md5_hash(
|
||||
json.dumps(
|
||||
[
|
||||
[str(path.relative_to(Path(local_path))), path.read_text()]
|
||||
for path in sorted(Path(local_path).rglob("*.py"))
|
||||
]
|
||||
)
|
||||
+ json.dumps({"entry": entry, "running_extra_volume": dict(running_extra_volume)})
|
||||
+ json.dumps({"extra_volumes": self.conf.extra_volumes})
|
||||
+ json.dumps(data_key)
|
||||
)
|
||||
if Path(target_folder / f"{key}.pkl").exists() and Path(target_folder / f"{key}.zip").exists():
|
||||
with open(target_folder / f"{key}.pkl", "rb") as f:
|
||||
ret: tuple[str, int] = pickle.load(f)
|
||||
self.unzip_a_file_into_a_folder(str(target_folder / f"{key}.zip"), local_path)
|
||||
else:
|
||||
ret = self.__run_ret_code_with_retry(entry, local_path, env, running_extra_volume, remove_timestamp)
|
||||
with open(target_folder / f"{key}.pkl", "wb") as f:
|
||||
pickle.dump(ret, f)
|
||||
self.zip_a_folder_into_a_file(local_path, str(target_folder / f"{key}.zip"))
|
||||
return ret
|
||||
|
||||
def run_ret_code(
|
||||
self,
|
||||
entry: str | None = None,
|
||||
local_path: str = ".",
|
||||
env: dict | None = None,
|
||||
**kwargs: dict,
|
||||
) -> tuple[str, int]:
|
||||
running_extra_volume = kwargs.get("running_extra_volume", {})
|
||||
if entry is None:
|
||||
entry = self.conf.default_entry
|
||||
|
||||
entry_add_timeout = (
|
||||
f"/bin/sh -c 'timeout {self.conf.running_timeout_period} {entry}; "
|
||||
f"entry_exit_code=$?; "
|
||||
f"chmod -R 777 {self.conf.mount_path}; "
|
||||
f"exit $entry_exit_code'"
|
||||
)
|
||||
|
||||
if self.conf.enable_cache:
|
||||
stdout, return_code = self.cached_run(entry_add_timeout, local_path, env, running_extra_volume)
|
||||
else:
|
||||
stdout, return_code = self.__run_ret_code_with_retry(
|
||||
entry_add_timeout, local_path, env, running_extra_volume, remove_timestamp=False
|
||||
)
|
||||
|
||||
return stdout, return_code
|
||||
|
||||
def dump_python_code_run_and_get_results(
|
||||
self,
|
||||
code: str,
|
||||
|
||||
+38
-3
@@ -6,11 +6,27 @@ from pathlib import Path
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
import shutil
|
||||
|
||||
from rdagent.utils.env import LocalConf, LocalEnv, QlibDockerConf, QTDockerEnv
|
||||
from rdagent.utils.env import (
|
||||
CondaConf,
|
||||
LocalConf,
|
||||
LocalEnv,
|
||||
QlibDockerConf,
|
||||
QTDockerEnv,
|
||||
)
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class QlibLocalEnv(LocalEnv):
|
||||
def prepare(self) -> None:
|
||||
if not (Path("~/.qlib/qlib_data/cn_data").expanduser().resolve().exists()):
|
||||
self.run(
|
||||
entry="python -m qlib.run.get_data qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn",
|
||||
)
|
||||
else:
|
||||
print("Data already exists. Download skipped.")
|
||||
|
||||
|
||||
class EnvUtils(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.test_workspace = DIRNAME / "test_workspace"
|
||||
@@ -24,16 +40,35 @@ class EnvUtils(unittest.TestCase):
|
||||
# NOTE: Because you need to download the data during the prepare process. So you need to have pyqlib in your environment.
|
||||
def test_local(self):
|
||||
local_conf = LocalConf(
|
||||
py_bin="/home/v-linlanglv/miniconda3/envs/RD-Agent-310/bin",
|
||||
bin_path="/home/v-linlanglv/miniconda3/envs/RD-Agent-310/bin",
|
||||
default_entry="qrun conf.yaml",
|
||||
)
|
||||
qle = LocalEnv(conf=local_conf)
|
||||
qle = QlibLocalEnv(conf=local_conf)
|
||||
qle.prepare()
|
||||
conf_path = str(DIRNAME / "env_tpl" / "conf.yaml")
|
||||
qle.run(entry="qrun " + conf_path)
|
||||
mlrun_p = DIRNAME / "env_tpl" / "mlruns"
|
||||
self.assertTrue(mlrun_p.exists(), f"Expected output file {mlrun_p} not found")
|
||||
|
||||
def test_local_simple(self):
|
||||
local_conf = LocalConf(bin_path="/home/xiaoyang/miniconda3/bin/", default_entry="which python")
|
||||
le = LocalEnv(conf=local_conf)
|
||||
print(local_conf)
|
||||
le.prepare()
|
||||
code_path = DIRNAME / "tmp_code"
|
||||
code_path.mkdir(exist_ok=True)
|
||||
res, code = le.run_ret_code(local_path=str(code_path))
|
||||
print(res, code)
|
||||
|
||||
def test_conda_simple(self):
|
||||
conda_conf = CondaConf(default_entry="which python", conda_env_name="MLE")
|
||||
le = LocalEnv(conf=conda_conf)
|
||||
le.prepare()
|
||||
code_path = DIRNAME / "tmp_code"
|
||||
code_path.mkdir(exist_ok=True)
|
||||
res, code = le.run_ret_code(local_path=str(code_path))
|
||||
print(res, code)
|
||||
|
||||
def test_docker(self):
|
||||
"""We will mount `env_tpl` into the docker image.
|
||||
And run the docker image with `qrun conf.yaml`
|
||||
|
||||
Reference in New Issue
Block a user