File size: 5,541 Bytes
0eca069 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | """Launch VisualWebArena / WebArena Docker containers for PoisonClaw.
Usage:
# Start VisualWebArena on port 9999
python scripts/launch_env.py --env visualwebarena --port 9999
# Start WebArena on port 8080
python scripts/launch_env.py --env webarena --port 8080
# Stop all running PoisonClaw containers
python scripts/launch_env.py --stop_all
"""
import argparse
import logging
import subprocess
import sys
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger("poisonclaw.launch_env")
# Docker image references for each environment
_DOCKER_IMAGES = {
"visualwebarena": {
"image": "ghcr.io/web-arena-x/visualwebarena:latest",
"services": {
"reddit": 9999,
"shopping": 7770,
"classifieds": 9980,
},
"docs": "https://github.com/web-arena-x/visualwebarena",
},
"webarena": {
"image": "ghcr.io/web-arena-x/webarena:latest",
"services": {
"gitlab": 8023,
"reddit": 9999,
"shopping": 7770,
"map": 3000,
},
"docs": "https://github.com/web-arena-x/webarena",
},
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Launch web environment Docker containers")
parser.add_argument(
"--env",
choices=list(_DOCKER_IMAGES.keys()),
help="Environment to launch",
)
parser.add_argument(
"--port",
type=int,
default=None,
help="Host port for the main service",
)
parser.add_argument(
"--stop_all",
action="store_true",
help="Stop all running PoisonClaw environment containers",
)
parser.add_argument(
"--status",
action="store_true",
help="Show status of running containers",
)
return parser.parse_args()
def check_docker() -> bool:
"""Check if Docker is available.
Returns:
True if Docker is installed and running.
"""
try:
result = subprocess.run(
["docker", "info"],
capture_output=True,
check=True,
timeout=10,
)
return True
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
logger.error(
"Docker is not available. Please install Docker to use web environments.\n"
"For local development without Docker, use stub environments by not setting "
"env.vwa_task_file in your config."
)
return False
def launch_env(env_name: str, port: int) -> None:
"""Launch the specified environment Docker container.
Args:
env_name: Environment name (``"visualwebarena"`` or ``"webarena"``).
port: Host port for the main service.
"""
if not check_docker():
sys.exit(1)
env_info = _DOCKER_IMAGES[env_name]
image = env_info["image"]
main_port = port or list(env_info["services"].values())[0]
logger.info("Launching %s (image: %s) on port %d...", env_name, image, main_port)
logger.info("Docs: %s", env_info["docs"])
# Build port mapping args
port_args = []
services = env_info["services"]
base_service_port = list(services.values())[0]
for service, service_port in services.items():
host_port = main_port + (service_port - base_service_port)
port_args += ["-p", f"{host_port}:{service_port}"]
cmd = [
"docker", "run", "-d",
"--name", f"poisonclaw-{env_name}",
*port_args,
image,
]
logger.info("Running: %s", " ".join(cmd))
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
container_id = result.stdout.strip()[:12]
logger.info(
"Container started: %s\n"
"Access at: http://localhost:%d",
container_id,
main_port,
)
except subprocess.CalledProcessError as exc:
logger.error("Failed to start container: %s", exc.stderr)
sys.exit(1)
def stop_all() -> None:
"""Stop all running PoisonClaw environment containers."""
if not check_docker():
return
result = subprocess.run(
["docker", "ps", "--filter", "name=poisonclaw-", "--format", "{{.Names}}"],
capture_output=True,
text=True,
)
containers = [c.strip() for c in result.stdout.splitlines() if c.strip()]
if not containers:
logger.info("No PoisonClaw containers running.")
return
for name in containers:
subprocess.run(["docker", "stop", name], check=False)
subprocess.run(["docker", "rm", name], check=False)
logger.info("Stopped and removed: %s", name)
def show_status() -> None:
"""Show status of running PoisonClaw containers."""
if not check_docker():
return
result = subprocess.run(
[
"docker", "ps",
"--filter", "name=poisonclaw-",
"--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}",
],
capture_output=True,
text=True,
)
print(result.stdout or "No PoisonClaw containers running.")
def main() -> None:
args = parse_args()
if args.stop_all:
stop_all()
elif args.status:
show_status()
elif args.env:
launch_env(args.env, args.port or 0)
else:
logger.error("Specify --env, --stop_all, or --status.")
sys.exit(1)
if __name__ == "__main__":
main()
|