DGX_AI / codeforge /sandbox /sandbox.py
vasiuuu's picture
Initial commit for CodeForge GRPO training
acf77ab
Raw
History Blame
3.54 kB
from __future__ import annotations
import shutil
import tempfile
from datetime import UTC, datetime
from pathlib import Path
from typing import TYPE_CHECKING
from codeforge.sandbox.imports import scan_imports
from codeforge.sandbox.metric import composite_score
from codeforge.sandbox.models import (
ImportReport,
ParsedResult,
SandboxResult,
ToolResult,
)
from codeforge.sandbox.runner import run_tool
from codeforge.sandbox.tools import (
DEFAULT_TOOLS,
argv_for,
is_available,
parse,
)
if TYPE_CHECKING:
from collections.abc import Iterable
def run_sandbox(
*,
project_dir: Path | None = None,
files: dict[str, str] | None = None,
tools: Iterable[str] = DEFAULT_TOOLS,
timeout_per_tool: float = 60.0,
) -> SandboxResult:
if (project_dir is None) == (files is None):
msg = "exactly one of project_dir / files must be set"
raise ValueError(msg)
tmp_root: Path | None = None
try:
if files is not None:
tmp_root = Path(tempfile.mkdtemp(prefix="codeforge_sandbox_"))
tmp_root_resolved = tmp_root.resolve()
for name, content in files.items():
target = (tmp_root / name).resolve()
if not target.is_relative_to(tmp_root_resolved):
msg = f"path escapes sandbox root: {name!r}"
raise ValueError(msg)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(content, encoding="utf-8")
project_dir = tmp_root
assert project_dir is not None
tool_list = tuple(tools)
tool_results: dict[str, ToolResult] = {}
parsed_results: dict[str, ParsedResult] = {}
imports_report: ImportReport | None = None
for name in tool_list:
if name == "imports":
imports_report = scan_imports(project_dir)
parsed_results[name] = ParsedResult(
ok=len(imports_report.unresolved) == 0,
count=len(imports_report.unresolved),
details={"unresolved": list(imports_report.unresolved)},
)
continue
if not is_available(name):
tool_results[name] = ToolResult(
name=name, argv=(name,), exit_code=-1,
stdout="", stderr="binary not found",
duration_ms=0, timed_out=False,
)
parsed_results[name] = ParsedResult(
ok=False, count=0, details={"unavailable": True},
)
continue
argv = argv_for(name, project_dir)
tr = run_tool(name, argv, cwd=project_dir, timeout=timeout_per_tool)
tool_results[name] = tr
parsed_results[name] = parse(name, tr)
if imports_report is None:
imports_report = ImportReport(total=0, unresolved=(), by_file={})
result = SandboxResult(
project_dir=str(project_dir),
tools_run=tool_list,
tool_results=tool_results,
parsed=parsed_results,
imports=imports_report,
composite_score=0.0,
generated_at=datetime.now(UTC).isoformat(timespec="seconds"),
)
score = composite_score(result)
return result.model_copy(update={"composite_score": score})
finally:
if tmp_root is not None and tmp_root.exists():
shutil.rmtree(tmp_root, ignore_errors=True)