devtools(swe-bench-pro): self-contained oboros-env volume builder + smoke docs

Add build_env_volume.sh to build the read-only oboros-env conda volume from this
repo's requirements.txt (dependencies only; Ouroboros source is seeded into
/obo-repo and imported via PYTHONPATH), so e1v2/run_pro.py no longer depends on an
externally hand-built environment volume. Document the env build, the single-task
smoke flow, and the local-eval gotchas (Colima DOCKER_HOST for the official
evaluator's docker SDK; rely on per-instance ours_output.json since the official
aggregator KeyErrors on FAIL_TO_PASS casing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ouroboros 2026-06-25 07:29:56 +03:00
parent d31d95a833
commit 7f0b35da70
2 changed files with 146 additions and 0 deletions

View file

@ -40,3 +40,32 @@ not broad config-like paths.
structurally suppressed cross-task self-evolution in older Ouroboros releases.
Use `e1v2/run_pro.py` / `e1v2/auto_run.py` for evolutionary runs and
`pro_predictions.py` for frozen prepared-repo predictions.
## Building the `oboros-env` volume (self-contained prerequisite for solve runs)
`e1v2/run_pro.py` mounts a read-only Docker volume `oboros-env` into each task image
at `/opt/miniconda3/envs/oboros`; that volume supplies the Python interpreter and
Ouroboros's third-party dependencies (the agent SOURCE is seeded separately into
`/obo-repo` and imported via `PYTHONPATH`, so the volume holds DEPENDENCIES ONLY).
`build_env_volume.sh` builds it self-contained from this repo's `requirements.txt`:
```bash
devtools/benchmarks/swe_bench_pro/build_env_volume.sh # idempotent; no-op if ready
devtools/benchmarks/swe_bench_pro/build_env_volume.sh --rebuild # force a clean rebuild
```
It creates a conda env directly at the volume prefix (self-contained, mountable into
arbitrary glibc `jefzda/sweap-images` task images) and builds for `--platform linux/amd64`
to match those images. Run it from a clean checkout (a dirty tree would bake uncommitted
edits' deps into the measured env). On macOS/Colima the source path must be under a
host-mounted directory (e.g. `/Users/...`), not `/tmp`.
## Single-task smoke
- Eval pipeline (no env volume needed): build a gold prediction from the evaluator's
`helper_code/sweap_eval_full_v2.jsonl` `patch` field and run `grade_pro.py`. A gold
patch must resolve (all `FAIL_TO_PASS` + `PASS_TO_PASS` pass).
- Full solve: `build_env_volume.sh`, then `e1v2/run_pro.py --start 1 --limit 1 --baseline
--reset-state --solve-model <model>`, then `grade_pro.py` on the produced patch.
- The Docker SDK used by the official evaluator ignores `docker context`; on Colima set
`DOCKER_HOST=unix://$HOME/.colima/default/docker.sock` for `grade_pro.py`.

View file

@ -0,0 +1,117 @@
#!/usr/bin/env bash
# Build the self-contained `oboros-env` Docker volume that e1v2/run_pro.py mounts
# read-only into each SWE-bench Pro task image at /opt/miniconda3/envs/oboros.
#
# WHY THIS EXISTS
# ---------------
# e1v2/entrypoint_pro.sh runs Ouroboros with OBO_PY=/opt/miniconda3/envs/oboros/bin/python
# and PYTHONPATH=/obo-repo. So the agent's SOURCE comes from the seeded /obo-repo volume,
# while the PYTHON INTERPRETER + third-party DEPENDENCIES come from this `oboros-env`
# volume. The volume was historically hand-built by an external kit; this script makes the
# devtools self-sufficient: it builds the env from THIS repo's requirements.txt, pinned to
# whatever checkout the script is run from.
#
# The env contains DEPENDENCIES ONLY (no `pip install -e .`): Ouroboros itself is imported
# from /obo-repo via PYTHONPATH, exactly like the runtime path.
#
# PORTABILITY
# -----------
# A conda env created directly at the prefix /opt/miniconda3/envs/oboros is self-contained
# (its own python + libs live under the prefix), so it mounts read-only into arbitrary
# glibc-based jefzda/sweap-images task containers. It is built with --platform linux/amd64
# to match those images (the env's compiled wheels must match the task image architecture).
#
# USAGE
# devtools/benchmarks/swe_bench_pro/build_env_volume.sh [--rebuild] [--volume NAME]
# [--python 3.11] [--platform linux/amd64] [--base-image condaforge/miniforge3:latest]
# [--src /path/to/ouroboros/repo]
#
# Idempotent: a no-op if the volume already has a working python, unless --rebuild is given.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
DEFAULT_SRC="$(cd "$SCRIPT_DIR/../../.." && pwd -P)" # repo root (devtools/benchmarks/swe_bench_pro -> repo)
VOLUME="oboros-env"
PYVER="3.11"
PLATFORM="linux/amd64"
BASE_IMAGE="condaforge/miniforge3:latest"
SRC="$DEFAULT_SRC"
PREFIX="/opt/miniconda3/envs/oboros"
REBUILD=0
while [ $# -gt 0 ]; do
case "$1" in
--rebuild) REBUILD=1; shift ;;
--volume) VOLUME="$2"; shift 2 ;;
--python) PYVER="$2"; shift 2 ;;
--platform) PLATFORM="$2"; shift 2 ;;
--base-image) BASE_IMAGE="$2"; shift 2 ;;
--src) SRC="$2"; shift 2 ;;
-h|--help) sed -n '1,40p' "$0"; exit 0 ;;
*) echo "unknown arg: $1" >&2; exit 2 ;;
esac
done
REQ="$SRC/requirements.txt"
[ -f "$REQ" ] || { echo "error: requirements.txt not found at $REQ (use --src)" >&2; exit 2; }
command -v docker >/dev/null 2>&1 || { echo "error: docker not found" >&2; exit 2; }
env_python_ok() {
docker run --rm -v "$VOLUME:$PREFIX:ro" --platform "$PLATFORM" --entrypoint "$PREFIX/bin/python" \
"$BASE_IMAGE" -c "import sys; print(sys.version)" >/dev/null 2>&1
}
if docker volume inspect "$VOLUME" >/dev/null 2>&1; then
if [ "$REBUILD" = 1 ]; then
echo "[build-env] --rebuild: removing existing volume '$VOLUME'"
docker volume rm -f "$VOLUME" >/dev/null
elif env_python_ok; then
echo "[build-env] volume '$VOLUME' already has a working python at $PREFIX; nothing to do (use --rebuild to force)"
exit 0
else
echo "[build-env] volume '$VOLUME' exists but has no working python; rebuilding into it"
docker volume rm -f "$VOLUME" >/dev/null
fi
fi
docker volume create "$VOLUME" >/dev/null
echo "[build-env] building $VOLUME from $SRC (python $PYVER, platform $PLATFORM, base $BASE_IMAGE)"
# Mount the volume AT the env prefix and create the conda env directly into it, so the volume
# content is exactly the env (run_pro mounts `oboros-env:/opt/miniconda3/envs/oboros:ro`).
docker run --rm --platform "$PLATFORM" \
-v "$VOLUME:$PREFIX" \
-v "$SRC:/src:ro" \
--entrypoint bash "$BASE_IMAGE" -euo pipefail -c '
PREFIX="'"$PREFIX"'"; PYVER="'"$PYVER"'"
echo "[build-env] conda create -p $PREFIX python=$PYVER"
conda create -y -p "$PREFIX" "python=$PYVER"
PIP="$PREFIX/bin/pip"
"$PIP" install --no-input --upgrade pip setuptools wheel
echo "[build-env] installing requirements.txt"
if ! "$PIP" install --no-input -r /src/requirements.txt; then
echo "[build-env] full requirements install failed; retrying without tree-sitter code-intel deps (lazy runtime import, degrades gracefully)"
grep -ivE "tree[-_]sitter" /src/requirements.txt > /tmp/reqs_no_treesitter.txt
"$PIP" install --no-input -r /tmp/reqs_no_treesitter.txt
fi
# Trim caches that bloat the read-only volume.
conda clean -y -a >/dev/null 2>&1 || true
find "$PREFIX" -type d -name __pycache__ -prune -exec rm -rf {} + 2>/dev/null || true
"$PREFIX/bin/python" --version
'
echo "[build-env] verifying core runtime deps import in the env"
docker run --rm -v "$VOLUME:$PREFIX:ro" --platform "$PLATFORM" --entrypoint "$PREFIX/bin/python" \
"$BASE_IMAGE" -c '
import importlib.util, sys
mods = ["httpx","starlette","uvicorn","websockets","openai","yaml","dulwich","PIL","huggingface_hub","claude_agent_sdk"]
optional = ["tree_sitter","tree_sitter_language_pack","mcp","playwright"]
missing=[m for m in mods if importlib.util.find_spec(m) is None]
opt_missing=[m for m in optional if importlib.util.find_spec(m) is None]
print("python", sys.version.split()[0])
print("required ok:", [m for m in mods if m not in missing])
if missing: print("MISSING REQUIRED:", missing); sys.exit(1)
if opt_missing: print("optional missing (ok, degrades):", opt_missing)
print("[build-env] OK")
'
echo "[build-env] done: volume '$VOLUME' is ready for e1v2/run_pro.py"