Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions swe_agent_local_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,35 @@
sys.meta_path = [f for f in sys.meta_path if type(f).__name__ != "_EditableFinder"]


def _install_local_environment_stubs() -> None:
"""Stub Docker-only imports that the local runner never executes."""
swe_env_module = "sweagent.environment.swe_env"
if swe_env_module not in sys.modules:
stub = ModuleType(swe_env_module)
stub.SWEEnv = object # type: ignore[attr-defined]
sys.modules[swe_env_module] = stub

utils_module = "sweagent.environment.utils"
if utils_module not in sys.modules:
utils_stub = ModuleType(utils_module)

def unavailable_copy(*_args: Any, **_kwargs: Any) -> None:
raise RuntimeError("container file copying is unavailable in local SWE-agent mode")

utils_stub.copy_anything_to_container = unavailable_copy # type: ignore[attr-defined]
sys.modules[utils_module] = utils_stub


def _load_sweagent_classes():
"""Load optional SWE-agent dependencies without mutating the environment."""
"""Load optional SWE-agent dependencies without Docker/dataset imports."""
try:
import anyio.to_thread # noqa: F401
import together # type: ignore[import-not-found] # noqa: F401

# Agent only uses SWEEnv in annotations; this runner supplies
# LocalSWEEnv at runtime. Importing the official SWEEnv would pull in
# swebench -> datasets -> pyarrow and add minutes of irrelevant native
# import work on Apple Silicon/Rosetta.
swe_env_module = "sweagent.environment.swe_env"
if swe_env_module not in sys.modules:
stub = ModuleType(swe_env_module)
stub.SWEEnv = object # type: ignore[attr-defined]
sys.modules[swe_env_module] = stub
# LocalSWEEnv and the configured Identity summarizer make these
# Docker-only modules unnecessary. Their real imports pull in
# swebench/datasets/numpy and can add minutes of irrelevant startup.
_install_local_environment_stubs()
from sweagent.agent.agents import ( # type: ignore[import-not-found]
Agent,
AgentArguments,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_swe_agent_local_runner.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import shlex
import sys
from pathlib import Path
from types import SimpleNamespace

Expand All @@ -9,6 +10,7 @@
from scripts.compare_three_systems import _claude_environment, build_goal_description
from swe_agent_local_runner import (
LocalSWEEnv,
_install_local_environment_stubs,
_materialize_agent_config,
_review_and_cleanup_changes,
)
Expand Down Expand Up @@ -58,6 +60,18 @@ def test_local_env_uses_per_task_command_venv(tmp_path):
env.close()


def test_local_runner_stubs_docker_only_swe_agent_modules(monkeypatch):
monkeypatch.delitem(sys.modules, "sweagent.environment.swe_env", raising=False)
monkeypatch.delitem(sys.modules, "sweagent.environment.utils", raising=False)

_install_local_environment_stubs()

assert sys.modules["sweagent.environment.swe_env"].SWEEnv is object
copy = sys.modules["sweagent.environment.utils"].copy_anything_to_container
with pytest.raises(RuntimeError, match="unavailable in local SWE-agent mode"):
copy(None, "source", "destination")


def test_inline_state_command_returns_json(tmp_path):
config_path = Path("swe_agent_local_config/default_local.yaml")
config = yaml.safe_load(config_path.read_text(encoding="utf-8"))
Expand Down
Loading