2025-02-11 20:50:19 +08:00
from pathlib import Path
2025-02-28 15:13:43 +08:00
from typing import Dict
2025-02-11 20:50:19 +08:00
import pandas as pd
from rdagent.app.data_science.conf import DS_RD_SETTING
from rdagent.components.coder import CoSTEER
from rdagent.components.coder.CoSTEER import CoSTEER
from rdagent.components.coder.CoSTEER.config import CoSTEER_SETTINGS
2025-02-16 01:40:44 +08:00
from rdagent.components.coder.CoSTEER.evaluators import (
CoSTEERMultiEvaluator ,
CoSTEERSingleFeedback ,
)
2025-02-11 20:50:19 +08:00
from rdagent.components.coder.CoSTEER.evolvable_subjects import FBWorkspace
from rdagent.components.coder.CoSTEER.evolving_strategy import (
CoSTEERQueriedKnowledge ,
MultiProcessEvolvingStrategy ,
)
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
2025-03-12 11:36:28 +08:00
from rdagent.components.coder.data_science.conf import get_ds_env
2025-04-09 23:24:12 +08:00
from rdagent.components.coder.data_science.share.eval import ModelDumpEvaluator
2025-02-11 20:50:19 +08:00
from rdagent.core.exception import RunnerError
from rdagent.core.scenario import Scenario
from rdagent.log import rdagent_logger as logger
2025-02-13 15:16:18 +08:00
from rdagent.oai.llm_utils import APIBackend , md5_hash
2025-02-11 20:50:19 +08:00
from rdagent.scenarios.data_science.dev.runner.eval import DSCoSTEERCoSTEEREvaluator
2025-03-18 17:43:44 +08:00
from rdagent.utils.agent.ret import PythonBatchEditOut
2025-02-11 20:50:19 +08:00
from rdagent.utils.agent.tpl import T
from rdagent.utils.env import DockerEnv , MLEBDockerConf
class DSRunnerMultiProcessEvolvingStrategy ( MultiProcessEvolvingStrategy ):
def implement_one_task (
self ,
target_task : CoSTEERTask ,
queried_knowledge : CoSTEERQueriedKnowledge | None = None ,
workspace : FBWorkspace | None = None ,
2025-02-16 01:40:44 +08:00
prev_task_feedback : CoSTEERSingleFeedback | None = None ,
2025-02-11 20:50:19 +08:00
) -> dict [ str , str ]:
2025-02-16 01:40:44 +08:00
if prev_task_feedback is None :
# if no prev_tak_feedback, it is the first loop; we do not make any changes and goto evaluators directly.
2025-02-11 20:50:19 +08:00
return {}
task_information_str = target_task . get_task_information ()
# 1. code
system_prompt = T ( ".prompts:DSCoSTEER_debugger.system" ) . r (
task_desc = task_information_str ,
2025-03-18 17:43:44 +08:00
out_spec = PythonBatchEditOut . get_spec ( with_del = False ),
2025-02-11 20:50:19 +08:00
)
user_prompt = T ( ".prompts:DSCoSTEER_debugger.user" ) . r (
code = workspace . all_codes ,
2025-02-16 01:40:44 +08:00
feedback = prev_task_feedback ,
2025-02-11 20:50:19 +08:00
)
2025-03-18 17:43:44 +08:00
batch_edit = PythonBatchEditOut . extract_output (
2025-02-11 20:50:19 +08:00
APIBackend () . build_messages_and_create_chat_completion (
user_prompt = user_prompt ,
system_prompt = system_prompt ,
)
)
batch_edit = { k : v for k , v in batch_edit . items () if k in workspace . file_dict . keys ()}
return batch_edit
def assign_code_list_to_evo ( self , code_list : list [ dict [ str , str ]], evo ):
"""
Assign the code list to the evolving item.
The code list is aligned with the evolving item's sub-tasks.
If a task is not implemented, put a None in the list.
"""
for index in range ( len ( evo . sub_tasks )):
if code_list [ index ] is None :
continue
if evo . sub_workspace_list [ index ] is None :
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
evo . sub_workspace_list [ index ] = evo . experiment_workspace
evo . sub_workspace_list [ index ] . inject_files ( ** code_list [ index ])
return evo
class DSCoSTEERRunner ( CoSTEER ):
def __init__ (
self ,
scen : Scenario ,
* args ,
** kwargs ,
) -> None :
2025-04-09 23:24:12 +08:00
eval_l = [ DSCoSTEERCoSTEEREvaluator ( scen = scen )]
if DS_RD_SETTING . enable_model_dump :
eval_l . append ( ModelDumpEvaluator ( scen = scen , data_type = "full" ))
2025-02-11 20:50:19 +08:00
eva = CoSTEERMultiEvaluator (
2025-04-09 23:24:12 +08:00
single_evaluator = eval_l , scen = scen
2025-02-11 20:50:19 +08:00
) # Please specify whether you agree running your eva in parallel or not
es = DSRunnerMultiProcessEvolvingStrategy ( scen = scen , settings = CoSTEER_SETTINGS )
2025-02-17 17:36:05 +08:00
# In runner, we don't need very big loops, so we set max_loop to 3
super () . __init__ (
2025-04-02 00:18:42 -06:00
* args ,
settings = CoSTEER_SETTINGS ,
eva = eva ,
es = es ,
evolving_version = 2 ,
scen = scen ,
max_loop = DS_RD_SETTING . runner_max_loop ,
** kwargs ,
2025-02-17 17:36:05 +08:00
)
2025-02-11 20:50:19 +08:00
def develop ( self , exp ):
bak_sub_tasks = exp . sub_tasks
exp . sub_tasks = [
CoSTEERTask (
name = "Debug running solution" ,
2025-02-12 12:35:17 +08:00
description = f "The whole workflow of the solution has finished with some execution error, please check the error message and debug the whole code repo. \n Current code repo md5: { md5_hash ( exp . experiment_workspace . all_codes ) } " ,
2025-02-11 20:50:19 +08:00
)
]
2025-04-09 13:34:48 +08:00
exp = super () . develop ( exp ) # run strategy(code implementation & evaluation loops)
2025-02-11 20:50:19 +08:00
exp . sub_tasks = bak_sub_tasks
2025-04-09 13:34:48 +08:00
# NOTE: after running the loops, we expect some results are generated
#
# 1) scores of the models and ensemble
2025-02-11 20:50:19 +08:00
score_fp = exp . experiment_workspace . workspace_path / "scores.csv"
if not score_fp . exists ():
logger . error ( "Metrics file (scores.csv) is not generated." )
raise RunnerError ( f "Metrics file (scores.csv) is not generated" )
exp . result = pd . read_csv ( score_fp , index_col = 0 )
2025-04-09 13:34:48 +08:00
# 2) if mle-bench, then the submission format checking will be used.
2025-02-11 20:50:19 +08:00
# DockerEnv for MLEBench submission validation
2025-04-09 13:34:48 +08:00
if DS_RD_SETTING . if_using_mle_data :
score_fp = exp . experiment_workspace . workspace_path / "test" / "mle_submission_format_test.output"
with score_fp . open () as f :
exp . format_check_result = f . read ()
2025-02-11 20:50:19 +08:00
return exp