Download backend/app/sandbox/executor.py from SevdanurGenc/LLM-Automated-Test-Generation-Agent: direct link, hf CLI and curl.
- Browser
- Download file 5.71 kB
-
https://huggingface.co/SevdanurGenc/LLM-Automated-Test-Generation-Agent/resolve/main/backend/app/sandbox/executor.py
- Command line
-
hf download hf://SevdanurGenc/LLM-Automated-Test-Generation-Agent/backend/app/sandbox/executor.py
-
curl -L -o executor.py https://huggingface.co/SevdanurGenc/LLM-Automated-Test-Generation-Agent/resolve/main/backend/app/sandbox/executor.py
5.71 kB
| """Komut çalıştırıcı: kodu yerel süreçte ya da izole Docker konteynerinde koşar. | |
| Ajanın derleyici ve test koşucusuna erişimi yalnızca bu katman üzerinden olur; | |
| böylece LLM'in ürettiği kod hiçbir zaman ana sürece doğrudan dokunmaz. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import shutil | |
| import signal | |
| import subprocess | |
| import time | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from ..config import settings | |
| class ExecResult: | |
| returncode: int | |
| stdout: str | |
| stderr: str | |
| duration_s: float | |
| timed_out: bool = False | |
| def combined(self) -> str: | |
| return (self.stdout + ("\n" + self.stderr if self.stderr else "")).strip() | |
| class Executor: | |
| """Soyut çalıştırıcı. `node_toolchain` ve `java_lib`, çalışma ortamı içindeki yollardır.""" | |
| mode = "abstract" | |
| node_toolchain: str | |
| java_lib: str | |
| def run(self, cmd: str, workdir: Path, timeout: int, image: str) -> ExecResult: # pragma: no cover | |
| raise NotImplementedError | |
| def workpath(self, workdir: Path) -> str: | |
| """Çalışma dizininin, komutun çalıştığı ortamdaki yolu.""" | |
| raise NotImplementedError | |
| class LocalExecutor(Executor): | |
| mode = "local" | |
| def __init__(self) -> None: | |
| self.node_toolchain = str(settings.node_toolchain) | |
| self.java_lib = str(settings.java_lib_dir) | |
| def workpath(self, workdir: Path) -> str: | |
| return str(workdir) | |
| def run(self, cmd: str, workdir: Path, timeout: int, image: str = "") -> ExecResult: | |
| env = dict(os.environ) | |
| env.update({"CI": "1", "NO_COLOR": "1", "FORCE_COLOR": "0", "PYTHONDONTWRITEBYTECODE": "1"}) | |
| env["NODE_PATH"] = str(Path(self.node_toolchain) / "node_modules") | |
| start = time.time() | |
| proc = subprocess.Popen( | |
| ["bash", "-lc", cmd], cwd=workdir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | |
| text=True, env=env, start_new_session=True, | |
| ) | |
| try: | |
| out, err = proc.communicate(timeout=timeout) | |
| return ExecResult(proc.returncode, out, err, time.time() - start) | |
| except subprocess.TimeoutExpired: | |
| try: | |
| os.killpg(proc.pid, signal.SIGKILL) | |
| except ProcessLookupError: | |
| pass | |
| out, err = proc.communicate() | |
| return ExecResult(-9, out or "", (err or "") + "\n[zaman aşımı]", time.time() - start, True) | |
| class DockerExecutor(Executor): | |
| """Her komut için ağ erişimi kapalı, bellek/CPU sınırlı geçici konteyner.""" | |
| mode = "docker" | |
| def __init__(self) -> None: | |
| self.node_toolchain = "/opt/toolchain" | |
| self.java_lib = "/opt/java-lib" | |
| # Backend bir konteynerde çalışıyorsa çalışma dosyaları sandbox'a iki yoldan biriyle ulaşır: | |
| # - WORK_VOLUME: backend ile paylaşılan adlandırılmış Docker birimi (her işletim sisteminde çalışır) | |
| # - HOST_WORK_ROOT: aynı dizinin host'taki yolu (bind mount) | |
| self.work_volume = os.environ.get("WORK_VOLUME") | |
| self.host_work_root = os.environ.get("HOST_WORK_ROOT") | |
| def workpath(self, workdir: Path) -> str: | |
| return str(workdir.resolve()) if self.work_volume else "/work" | |
| def _mount_args(self, workdir: Path) -> list[str]: | |
| if self.work_volume: | |
| root = str(settings.work_root.resolve()) | |
| return ["-v", f"{self.work_volume}:{root}", "-w", str(workdir.resolve())] | |
| return ["-v", f"{self._host_path(workdir)}:/work", "-w", "/work"] | |
| def _host_path(self, workdir: Path) -> str: | |
| if self.host_work_root: | |
| rel = workdir.resolve().relative_to(settings.work_root.resolve()) | |
| return str(Path(self.host_work_root) / rel) | |
| return str(workdir.resolve()) | |
| def _open_permissions(workdir: Path) -> None: | |
| # Sandbox konteyneri ayrıcalıksız kullanıcıyla çalışır; çalışma dizinine yazabilmeli. | |
| for p in [workdir, *workdir.rglob("*")]: | |
| try: | |
| p.chmod(0o777 if p.is_dir() else 0o666) | |
| except OSError: | |
| pass | |
| def run(self, cmd: str, workdir: Path, timeout: int, image: str) -> ExecResult: | |
| self._open_permissions(workdir) | |
| name = f"lta-{workdir.name}-{int(time.time() * 1000) % 100000}" | |
| docker_cmd = [ | |
| "docker", "run", "--rm", "--name", name, "--network", "none", | |
| "--memory", settings.docker_memory, "--cpus", settings.docker_cpus, | |
| "--pids-limit", "256", "--security-opt", "no-new-privileges", | |
| *self._mount_args(workdir), | |
| "-e", "CI=1", "-e", "NO_COLOR=1", | |
| image, "bash", "-lc", cmd, | |
| ] | |
| start = time.time() | |
| try: | |
| p = subprocess.run(docker_cmd, capture_output=True, text=True, timeout=timeout + 15) | |
| return ExecResult(p.returncode, p.stdout, p.stderr, time.time() - start) | |
| except subprocess.TimeoutExpired as e: | |
| subprocess.run(["docker", "kill", name], capture_output=True) | |
| return ExecResult(-9, (e.stdout or b"").decode(errors="ignore") if isinstance(e.stdout, bytes) else (e.stdout or ""), | |
| "[zaman aşımı]", time.time() - start, True) | |
| _executor: Executor | None = None | |
| def get_executor() -> Executor: | |
| global _executor | |
| if _executor is None: | |
| if settings.sandbox_mode == "docker": | |
| if not shutil.which("docker"): | |
| raise RuntimeError("SANDBOX_MODE=docker fakat docker komutu bulunamadı") | |
| _executor = DockerExecutor() | |
| else: | |
| _executor = LocalExecutor() | |
| return _executor | |