# Copyright (c) Microsoft. All rights reserved. import argparse import os import re import subprocess import time from omegaconf import OmegaConf from agentlightning import Trainer from agentlightning.algorithm.verl import VERL from contrib.agentlightning.contrib.algorithm.env_verl.daemon import EnvAgentModeDaemon from contrib.agentlightning.contrib.algorithm.env_verl.trainer import EnvAgentLightningTrainer def run_cmd(cmd): """Execute a shell command and print its output""" print(f"👉 Running: {cmd}") result = subprocess.run(cmd, shell=True, text=True, capture_output=True) if result.stdout: print(result.stdout) if result.stderr: print(result.stderr) return result def kill_process_on_port(port): result = subprocess.run(f"sudo lsof -t -i :{port}", shell=True, capture_output=True, text=True) pids = result.stdout.strip().split("\n") for pid in pids: if pid: print(f"🔪 Killing process {pid} on port {port}") subprocess.run(f"sudo kill -9 {pid}", shell=True) def train_val_dataset(cfg): """Load training and validation datasets from parquet files.""" from datasets import Dataset train_data = Dataset.from_parquet(cfg["data"]["train_files"]) val_data = Dataset.from_parquet(cfg["data"]["val_files"]) return train_data, val_data def get_config(path): cfg = OmegaConf.load(path) OmegaConf.resolve(cfg) if "variables" in cfg: del cfg["variables"] return cfg if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--env", type=str, default="scienceworld2") parser.add_argument("--algorithm", type=str, default="empo2_qwen_7b_instruct") parser.add_argument("--n_workers", type=int, default=4, help="Number of workers for training") parser.add_argument("--trial", type=int, default=0, help="Number of trials") parser.add_argument("--task_num", type=int, default=25, help="ScienceWorld Task number to inject as env var") parser.add_argument("--_background", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() # Kill any leftover processes from previous runs (excluding the current process) current_pid = os.getpid() run_cmd(f"pgrep -f train_env_agent.py | grep -vxF '{current_pid}' | xargs -r kill -9") run_cmd("pkill -9 -f server_bert.py") run_cmd("pkill -9 -f server_mem.py") kill_process_on_port(8000) kill_process_on_port(8001) # Restart Ray cluster cleanly kill_process_on_port(4747) run_cmd("pkill -f AgentLightning") run_cmd("ray stop") time.sleep(2) run_cmd("env RAY_DEBUG=legacy HYDRA_FULL_ERROR=1 VLLM_USE_V1=1 ray start --head --dashboard-host=0.0.0.0") # set environment variable before loading configs os.environ["TRIAL"] = str(args.trial) if "scienceworld" in args.env: os.environ["TASK_NUM"] = str(args.task_num) # Load configs agent_config_path = f"config_env/{args.env}.yaml" agent_config = get_config(agent_config_path) env_prefix = re.sub(r"\d+$", "", args.env) trainer_config_path = f"config_verl/{env_prefix}/{args.algorithm}.yaml" if "gigpo" in args.algorithm: agent_config.log_env_obs = True rl_training_config = get_config(trainer_config_path) # Load datasets train_dataset, val_dataset = train_val_dataset(rl_training_config) # Initialize agent if "empo2" in args.algorithm: from contrib.agentlightning.contrib.agent.empo2_agent import EMPO2Agent, reset_memory os.makedirs("logs", exist_ok=True) subprocess.Popen(f"nohup python empo2_server/server_bert.py > logs/bert_{args.task_num}.log 2>&1 &", shell=True) subprocess.Popen(f"nohup python empo2_server/server_mem.py > logs/mem_{args.task_num}.log 2>&1 &", shell=True) NUM_MEMORY = 5 time.sleep(1) reset_memory(NUM_MEMORY) agent = EMPO2Agent(agent_config) else: from contrib.agentlightning.contrib.agent.env_agent import EnvAgent agent = EnvAgent(agent_config) # Initialize trainer and start training trainer = Trainer( algorithm=VERL( config=rl_training_config, trainer_cls=EnvAgentLightningTrainer, daemon_cls=EnvAgentModeDaemon, ), n_workers=args.n_workers, ) trainer.fit(agent, train_dataset, val_dataset=val_dataset)