swe_agentic: auto-release pulled images at process exit

Per-instance images are 1-4GB each; a --limit 10 run pulls up to 10 of
them and previously stranded the whole footprint on Ctrl+C/exit. Every
image THIS process pulls is now registered and released by an atexit
hook (containers first, then rmi) -- images that already existed
locally are never touched. EVALHARNESS_KEEP_SWE_IMAGES=1 opts out for
prefetch-style runs that want to keep them.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sora 2026-09-18 09:03:17 +00:00
parent f09fe184fe
commit e9e3230e46

View File

@ -13,12 +13,56 @@ import asyncio
import json import json
import re import re
import subprocess import subprocess
import sys
import uuid import uuid
from typing import Any, Dict from typing import Any, Dict
from ...data.sample import ChatMessage, Sample from ...data.sample import ChatMessage, Sample
from ..loop import Environment, register_env from ..loop import Environment, register_env
# ---- image lifecycle tracking -------------------------------------------
# per-instance swe images are BIG (1-4GB each, 500 total ~2TB): every
# image THIS process pulled is registered here and released at exit
# (normal end, Ctrl+C kill, crash) so a run never strands its footprint.
import atexit as _atexit
_IMAGES_PULLED: list = []
def _release_pulled_images() -> None:
import os as _os
if not _IMAGES_PULLED or _os.environ.get('EVALHARNESS_KEEP_SWE_IMAGES'):
return
# in-use containers first (docker refuses rmi otherwise)
try:
_docker(['ps', '-aq', '--filter', 'name=eh-swe-'], timeout=30)
except Exception:
pass
try:
_docker(['ps', '-aq', '--filter', 'ancestor=none'], timeout=30)
except Exception:
pass
import subprocess as _sp
try:
ids = _sp.run(['docker', 'ps', '-aq', '--filter', 'name=eh-swe'],
capture_output=True, text=True, timeout=30).stdout.split()
if ids:
_docker(['rm', '-f'] + ids, timeout=120)
except Exception:
pass
for img in _IMAGES_PULLED:
try:
_docker(['rmi', '-f', img], timeout=120)
print(f'· released swe image {img}', file=sys.stderr, flush=True)
except Exception:
pass
_atexit.register(_release_pulled_images)
# ------------------------------------------------------------------------
SENTINEL = 'COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT' SENTINEL = 'COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT'
# mini-swe-agent swebench.yaml contract (verbatim sections that matter) # mini-swe-agent swebench.yaml contract (verbatim sections that matter)
@ -123,10 +167,13 @@ class SWEAgenticEnvironment(Environment):
# output and its 120s timeout kills runs on slow mirrors # output and its 120s timeout kills runs on slow mirrors
from ...sandbox.docker import ensure_image from ...sandbox.docker import ensure_image
had_it = _docker(['image', 'inspect', image]).returncode == 0
try: try:
ensure_image(image) ensure_image(image)
except RuntimeError as e: except RuntimeError as e:
raise RuntimeError(f'image {image} unavailable: {str(e)[:150]}') raise RuntimeError(f'image {image} unavailable: {str(e)[:150]}')
if not had_it and image not in _IMAGES_PULLED:
_IMAGES_PULLED.append(image) # we pulled it -> we release it
name = f'eh-swe-{uuid.uuid4().hex[:10]}' name = f'eh-swe-{uuid.uuid4().hex[:10]}'
r = _docker(['run', '-d', '--name', name, r = _docker(['run', '-d', '--name', name,
'-w', '/testbed', '-w', '/testbed',