Skip to content

Commit 192c2ab

Browse files
authored
Merge pull request #34 from cooperbench/mini_Swe_agent_v2
Adding support for minisweagent_v2
2 parents f810137 + f06c319 commit 192c2ab

33 files changed

+2910
-3
lines changed

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ swe-agent = [
8585
"unidiff>=0.7",
8686
]
8787
gcp = [
88+
"cooperbench",
8889
"google-cloud-batch>=0.17",
8990
"google-cloud-compute>=1.0",
9091
"google-cloud-storage>=2.0",
@@ -186,7 +187,7 @@ exclude_lines = [
186187
]
187188

188189
[tool.uv.sources]
189-
cooperbench = { workspace = true }
190190
openhands-sdk = { path = "src/cooperbench/agents/openhands_agent_sdk/openhands-sdk", editable = true }
191191
openhands-tools = { path = "src/cooperbench/agents/openhands_agent_sdk/openhands-tools", editable = true }
192192
openhands-workspace = { path = "src/cooperbench/agents/openhands_agent_sdk/openhands-workspace", editable = true }
193+
cooperbench = { workspace = true }

src/cooperbench/agents/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def run(
104104
# Add your agent's shorthand here when registering a new adapter
105105
AGENT_SHORTHANDS = {
106106
"mini_swe_agent": "msa",
107+
"mini_swe_agent_v2": "msa_v2",
107108
"swe_agent": "sw",
108109
"openhands_sdk": "oh",
109110
}

src/cooperbench/agents/mini_swe_agent/environments/modal.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ def _get_or_build_image(image_name: str) -> modal.Image:
6969
if image_name in _image_cache:
7070
return _image_cache[image_name]
7171

72-
# Build and cache (CACHE_BUST env forces new image hash) TODO: @arpan remove this after testing
73-
image = modal.Image.from_registry(image_name).entrypoint([]).env({"COOPERBENCH_CACHE": "v6"})
72+
image = modal.Image.from_registry(image_name).entrypoint([])
7473
_image_cache[image_name] = image
7574
return image
7675

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
mini-swe-agent v2 - A Minimal AI Agent for Software Engineering (Tool-calling version)
3+
4+
Source: https://github.com/SWE-agent/mini-swe-agent
5+
Version: 2.1.0 (commit 56613dd)
6+
License: MIT
7+
Copyright (c) 2025 Kilian A. Lieret and Carlos E. Jimenez
8+
9+
This code is copied directly from mini-swe-agent with modifications:
10+
- Import paths changed from 'minisweagent' to 'cooperbench.agents.mini_swe_agent_v2'
11+
- Removed text-based (regex) action parsing (v1 compat), keeping only tool calling
12+
- Removed interactive agent, non-Docker environments, non-litellm models
13+
- Added inter-agent communication (messaging + git connectors) for CooperBench
14+
15+
Citation:
16+
@misc{minisweagent2025,
17+
title={mini-swe-agent: A Minimal AI Agent for Software Engineering},
18+
author={Lieret, Kilian A. and Jimenez, Carlos E.},
19+
year={2025},
20+
url={https://github.com/SWE-agent/mini-swe-agent}
21+
}
22+
23+
This file provides:
24+
- Path settings for global config file & relative directories
25+
- Version numbering
26+
- Protocols for the core components of mini-swe-agent.
27+
"""
28+
29+
__version__ = "2.1.0"
30+
31+
import os
32+
from pathlib import Path
33+
from typing import Any, Protocol
34+
35+
import dotenv
36+
from platformdirs import user_config_dir
37+
38+
from cooperbench.agents.mini_swe_agent_v2.utils.log import logger
39+
40+
package_dir = Path(__file__).resolve().parent
41+
42+
global_config_dir = Path(os.getenv("MSWEA_GLOBAL_CONFIG_DIR") or user_config_dir("mini-swe-agent"))
43+
global_config_dir.mkdir(parents=True, exist_ok=True)
44+
global_config_file = Path(global_config_dir) / ".env"
45+
46+
dotenv.load_dotenv(dotenv_path=global_config_file)
47+
48+
49+
# === Protocols ===
50+
# You can ignore them unless you want static type checking.
51+
52+
53+
class Model(Protocol):
54+
"""Protocol for language models."""
55+
56+
config: Any
57+
58+
def query(self, messages: list[dict[str, str]], **kwargs) -> dict: ...
59+
60+
def format_message(self, **kwargs) -> dict: ...
61+
62+
def format_observation_messages(
63+
self, message: dict, outputs: list[dict], template_vars: dict | None = None
64+
) -> list[dict]: ...
65+
66+
def get_template_vars(self, **kwargs) -> dict[str, Any]: ...
67+
68+
def serialize(self) -> dict: ...
69+
70+
71+
class Environment(Protocol):
72+
"""Protocol for execution environments."""
73+
74+
config: Any
75+
76+
def execute(self, action: dict, cwd: str = "") -> dict[str, Any]: ...
77+
78+
def get_template_vars(self, **kwargs) -> dict[str, Any]: ...
79+
80+
def serialize(self) -> dict: ...
81+
82+
83+
class Agent(Protocol):
84+
"""Protocol for agents."""
85+
86+
config: Any
87+
88+
def run(self, task: str, **kwargs) -> dict: ...
89+
90+
def save(self, path: Path | None, *extra_dicts) -> dict: ...
91+
92+
93+
__all__ = [
94+
"Agent",
95+
"Model",
96+
"Environment",
97+
"package_dir",
98+
"__version__",
99+
"global_config_file",
100+
"global_config_dir",
101+
"logger",
102+
]
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""Mini-SWE-Agent v2 adapter for CooperBench.
2+
3+
This adapter wraps the mini-swe-agent v2 framework (tool-calling version)
4+
to conform to the AgentRunner interface used by CooperBench.
5+
"""
6+
7+
import yaml
8+
9+
from cooperbench.agents import AgentResult
10+
from cooperbench.agents.mini_swe_agent_v2.agents.default import DefaultAgent
11+
from cooperbench.agents.mini_swe_agent_v2.config import get_config_path
12+
from cooperbench.agents.mini_swe_agent_v2.connectors import GitConnector
13+
from cooperbench.agents.mini_swe_agent_v2.connectors.messaging import MessagingConnector
14+
from cooperbench.agents.mini_swe_agent_v2.models.litellm_model import LitellmModel
15+
from cooperbench.agents.mini_swe_agent_v2.models.utils.actions_toolcall import SEND_MESSAGE_TOOL
16+
from cooperbench.agents.registry import register
17+
18+
19+
@register("mini_swe_agent_v2")
20+
class MiniSweAgentV2Runner:
21+
"""Adapter for mini-swe-agent v2 framework (tool-calling)."""
22+
23+
def run(
24+
self,
25+
task: str,
26+
image: str,
27+
*,
28+
agent_id: str = "agent",
29+
model_name: str = "gpt-4o",
30+
agents: list[str] | None = None,
31+
comm_url: str | None = None,
32+
git_server_url: str | None = None,
33+
git_enabled: bool = False,
34+
messaging_enabled: bool = True,
35+
config: dict | None = None,
36+
agent_config: str | None = None,
37+
log_dir: str | None = None,
38+
) -> AgentResult:
39+
"""Run mini-swe-agent v2 on a task."""
40+
# Always load default config, then merge with any overrides
41+
config_path = get_config_path("mini")
42+
with open(config_path) as f:
43+
default_config = yaml.safe_load(f)
44+
45+
# Merge passed config overrides into default config
46+
if config is not None:
47+
default_config.update(config)
48+
49+
agent_cfg = default_config.get("agent", {})
50+
model_cfg = default_config.get("model", {})
51+
env_cfg = default_config.get("environment", {})
52+
backend = default_config.get("backend", "modal")
53+
54+
# Create environment based on backend
55+
env_kwargs = {
56+
"image": image,
57+
"cwd": "/workspace/repo",
58+
"timeout": 3600,
59+
}
60+
if env_cfg.get("env"):
61+
env_kwargs["env"] = env_cfg["env"]
62+
63+
if backend == "docker":
64+
from cooperbench.agents.mini_swe_agent_v2.environments.docker import DockerEnvironment
65+
66+
if config and config.get("git_network"):
67+
env_kwargs["network"] = config["git_network"]
68+
env = DockerEnvironment(**env_kwargs)
69+
else:
70+
from cooperbench.agents.mini_swe_agent_v2.environments.modal import ModalEnvironment
71+
72+
env = ModalEnvironment(**env_kwargs)
73+
74+
# Capture base commit for patch generation
75+
base_commit_result = env.execute({"command": "git rev-parse HEAD"})
76+
base_commit = base_commit_result.get("output", "").strip()
77+
78+
# Setup messaging connector if enabled
79+
comm = None
80+
use_messaging = messaging_enabled and comm_url and agents and len(agents) > 1
81+
if use_messaging:
82+
comm = MessagingConnector(agent_id=agent_id, agents=agents, url=comm_url)
83+
84+
# Create LLM model with send_message tool if messaging is enabled
85+
extra_tools = [SEND_MESSAGE_TOOL] if use_messaging else None
86+
model = LitellmModel(model_name=model_name, extra_tools=extra_tools, **model_cfg)
87+
88+
# Setup git connector if enabled
89+
if git_enabled and git_server_url and agents:
90+
git_connector = GitConnector(
91+
agent_id=agent_id,
92+
agents=agents,
93+
server_url=git_server_url,
94+
)
95+
git_connector.setup(env)
96+
97+
# Create agent with template variables for collaboration
98+
extra_vars = {
99+
"agent_id": agent_id if (agents and len(agents) > 1) else None,
100+
"agents": agents if agents else [],
101+
"git_enabled": git_enabled,
102+
"messaging_enabled": messaging_enabled,
103+
}
104+
105+
agent = DefaultAgent(
106+
model=model,
107+
env=env,
108+
comm=comm,
109+
agent_id=agent_id,
110+
**agent_cfg,
111+
)
112+
agent.extra_template_vars.update(extra_vars)
113+
114+
# Run agent
115+
error_msg = None
116+
try:
117+
result = agent.run(task=task)
118+
status = result.get("exit_status", "Submitted")
119+
except Exception as e:
120+
status = "Error"
121+
error_msg = str(e)
122+
123+
# Extract patch (committed + uncommitted changes)
124+
patch = self._get_patch(env, base_commit)
125+
126+
# Cleanup
127+
env.cleanup()
128+
129+
return AgentResult(
130+
status=status,
131+
patch=patch,
132+
cost=agent.cost,
133+
steps=agent.n_calls,
134+
messages=agent.messages,
135+
sent_messages=agent.sent_messages,
136+
error=error_msg,
137+
)
138+
139+
def _get_patch(self, env, base_commit: str) -> str:
140+
"""Extract git diff from base commit to current working tree state."""
141+
try:
142+
result = env.execute({"command": f"git diff {base_commit}"})
143+
return result.get("output", "").strip()
144+
except Exception:
145+
return ""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Agent implementations for mini-SWE-agent v2."""

0 commit comments

Comments
 (0)