Skip to content

ConTree

ConTree Environment class

Full source code
import logging
import platform
import shlex
from dataclasses import asdict, is_dataclass, replace
from numbers import Number
from typing import Any, TypedDict

from contree_sdk import ContreeSync
from contree_sdk.config import ContreeConfig
from contree_sdk.sdk.objects.image import ContreeImageSync
from pydantic import BaseModel

from minisweagent import Environment
from minisweagent.exceptions import Submitted
from minisweagent.utils.serialize import recursive_merge

logger = logging.getLogger(__name__)


class ContreeEnvironmentConfig(BaseModel):
    contree_config: ContreeConfig | dict[str, Any]

    image: str
    image_tag: str = None
    """If set, used to pull image by tag. If fails, then it imports by `image` and sets `image_tag` value to image tag"""
    cwd: str = "/"
    """Working directory in which to execute commands."""
    cwd_auto_create: bool = True
    """Create cwd before running any commands."""
    env: dict[str, str] = {}
    """Environment variables to set in the container."""
    forward_env: list[str] = []
    """Environment variables to forward to the container.
    Variables are only forwarded if they are set in the host environment.
    In case of conflict with `env`, the `env` variables take precedence.
    """
    interpreter: list[str] = ["bash", "-c"]
    """Interpreter to execute commands"""
    timeout: int = 100
    """Timeout for executing commands in the container."""
    import_username: str | None = None
    """Username that will be used if image needs to be imported."""
    import_password: str | None = None
    """Password that will be used if image needs to be imported."""


class ExecutionResult(TypedDict):
    output: str
    returncode: int


class ContreeEnvironment(Environment):
    def __init__(self, *, config_class: type[ContreeEnvironmentConfig] = ContreeEnvironmentConfig, **kwargs):
        """This class executes bash commands in a [ConTree](https://contree.dev) container
        using [contree-sdk](https://github.com/nebius/contree-sdk)"""

        self.config: ContreeEnvironmentConfig = config_class(**kwargs)
        self.logger = logging.getLogger("minisweagent.environment")

        if isinstance(self.config.contree_config, dict):
            self.config = replace(self.config, contree_config=ContreeConfig(**self.config.contree_config))

        self.client = ContreeSync(config=self.config.contree_config)
        self.session = self._pull_image().session()
        if self.config.cwd_auto_create:
            self.execute(
                action={"command": f"mkdir -p {self.config.cwd}"},
                cwd="/",
            )

    def _pull_image(self) -> ContreeImageSync:
        return self.client.images.oci(
            self.config.image,
            tag=self.config.image_tag,
            username=self.config.import_username,
            password=self.config.import_password,
        )

    def _shell_command(self, command: str) -> str:
        shell_cmd = " ".join(self.config.interpreter)
        return f"{shell_cmd} {shlex.quote(command)}"

    def execute(self, action: dict, cwd: str = "", *, timeout: int | None = None) -> dict[str, Any]:
        """Execute a command in the environment and return the raw output."""
        command = action.get("command")
        self.session.run(
            shell=self._shell_command(command),
            cwd=cwd or self.config.cwd,
            timeout=timeout or self.config.timeout,
            disposable=False,
        ).wait()

        cwd = cwd or self.config.cwd
        try:
            self.session.run(
                shell=self._shell_command(command),
                cwd=cwd or self.config.cwd,
                timeout=timeout or self.config.timeout,
                disposable=False,
            ).wait()
            output = {
                "output": self.session.stdout + self.session.stderr,
                "returncode": self.session.exit_code,
                "exception_info": "",
            }
        except Exception as e:
            raw_output = getattr(e, "output", None)
            raw_output = (
                raw_output.decode("utf-8", errors="replace") if isinstance(raw_output, bytes) else (raw_output or "")
            )
            extras = {}
            if is_dataclass(e):
                extras = {k: str(v) if not isinstance(v, Number) else v for k, v in asdict(e).items()}

            output = {
                "output": raw_output,
                "returncode": -1,
                "exception_info": f"An error occurred while executing the command: {e}",
                "extra": {"exception_type": type(e).__name__, "exception": str(e), **extras},
            }
        self._check_finished(output)
        return output

    def _check_finished(self, output: dict):
        """Raises Submitted if the output indicates task completion."""
        lines = output.get("output", "").lstrip().splitlines(keepends=True)
        if lines and lines[0].strip() == "COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT" and output["returncode"] == 0:
            submission = "".join(lines[1:])
            raise Submitted(
                {
                    "role": "exit",
                    "content": submission,
                    "extra": {"exit_status": "Submitted", "submission": submission},
                }
            )

    def get_template_vars(self, **kwargs) -> dict[str, Any]:
        return recursive_merge(self.config.model_dump(), platform.uname()._asdict(), kwargs)

    def serialize(self) -> dict:
        return {
            "info": {
                "config": {
                    "environment": self.config.model_dump(mode="json"),
                    "environment_type": f"{self.__class__.__module__}.{self.__class__.__name__}",
                }
            }
        }

minisweagent.environments.extra.contree

logger module-attribute

logger = getLogger(__name__)

ContreeEnvironmentConfig

Bases: BaseModel

contree_config instance-attribute

contree_config: ContreeConfig | dict[str, Any]

image instance-attribute

image: str

image_tag class-attribute instance-attribute

image_tag: str = None

If set, used to pull image by tag. If fails, then it imports by image and sets image_tag value to image tag

cwd class-attribute instance-attribute

cwd: str = '/'

Working directory in which to execute commands.

cwd_auto_create class-attribute instance-attribute

cwd_auto_create: bool = True

Create cwd before running any commands.

env class-attribute instance-attribute

env: dict[str, str] = {}

Environment variables to set in the container.

forward_env class-attribute instance-attribute

forward_env: list[str] = []

Environment variables to forward to the container. Variables are only forwarded if they are set in the host environment. In case of conflict with env, the env variables take precedence.

interpreter class-attribute instance-attribute

interpreter: list[str] = ['bash', '-c']

Interpreter to execute commands

timeout class-attribute instance-attribute

timeout: int = 100

Timeout for executing commands in the container.

import_username class-attribute instance-attribute

import_username: str | None = None

Username that will be used if image needs to be imported.

import_password class-attribute instance-attribute

import_password: str | None = None

Password that will be used if image needs to be imported.

ExecutionResult

Bases: TypedDict

output instance-attribute

output: str

returncode instance-attribute

returncode: int

ContreeEnvironment

ContreeEnvironment(
    *,
    config_class: type[
        ContreeEnvironmentConfig
    ] = ContreeEnvironmentConfig,
    **kwargs,
)

Bases: Environment

This class executes bash commands in a ConTree container using contree-sdk

Source code in src/minisweagent/environments/extra/contree.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def __init__(self, *, config_class: type[ContreeEnvironmentConfig] = ContreeEnvironmentConfig, **kwargs):
    """This class executes bash commands in a [ConTree](https://contree.dev) container
    using [contree-sdk](https://github.com/nebius/contree-sdk)"""

    self.config: ContreeEnvironmentConfig = config_class(**kwargs)
    self.logger = logging.getLogger("minisweagent.environment")

    if isinstance(self.config.contree_config, dict):
        self.config = replace(self.config, contree_config=ContreeConfig(**self.config.contree_config))

    self.client = ContreeSync(config=self.config.contree_config)
    self.session = self._pull_image().session()
    if self.config.cwd_auto_create:
        self.execute(
            action={"command": f"mkdir -p {self.config.cwd}"},
            cwd="/",
        )

config instance-attribute

config: ContreeEnvironmentConfig = config_class(**kwargs)

logger instance-attribute

logger = getLogger('minisweagent.environment')

client instance-attribute

client = ContreeSync(config=contree_config)

session instance-attribute

session = session()

execute

execute(
    action: dict,
    cwd: str = "",
    *,
    timeout: int | None = None,
) -> dict[str, Any]

Execute a command in the environment and return the raw output.

Source code in src/minisweagent/environments/extra/contree.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def execute(self, action: dict, cwd: str = "", *, timeout: int | None = None) -> dict[str, Any]:
    """Execute a command in the environment and return the raw output."""
    command = action.get("command")
    self.session.run(
        shell=self._shell_command(command),
        cwd=cwd or self.config.cwd,
        timeout=timeout or self.config.timeout,
        disposable=False,
    ).wait()

    cwd = cwd or self.config.cwd
    try:
        self.session.run(
            shell=self._shell_command(command),
            cwd=cwd or self.config.cwd,
            timeout=timeout or self.config.timeout,
            disposable=False,
        ).wait()
        output = {
            "output": self.session.stdout + self.session.stderr,
            "returncode": self.session.exit_code,
            "exception_info": "",
        }
    except Exception as e:
        raw_output = getattr(e, "output", None)
        raw_output = (
            raw_output.decode("utf-8", errors="replace") if isinstance(raw_output, bytes) else (raw_output or "")
        )
        extras = {}
        if is_dataclass(e):
            extras = {k: str(v) if not isinstance(v, Number) else v for k, v in asdict(e).items()}

        output = {
            "output": raw_output,
            "returncode": -1,
            "exception_info": f"An error occurred while executing the command: {e}",
            "extra": {"exception_type": type(e).__name__, "exception": str(e), **extras},
        }
    self._check_finished(output)
    return output

get_template_vars

get_template_vars(**kwargs) -> dict[str, Any]
Source code in src/minisweagent/environments/extra/contree.py
137
138
def get_template_vars(self, **kwargs) -> dict[str, Any]:
    return recursive_merge(self.config.model_dump(), platform.uname()._asdict(), kwargs)

serialize

serialize() -> dict
Source code in src/minisweagent/environments/extra/contree.py
140
141
142
143
144
145
146
147
148
def serialize(self) -> dict:
    return {
        "info": {
            "config": {
                "environment": self.config.model_dump(mode="json"),
                "environment_type": f"{self.__class__.__module__}.{self.__class__.__name__}",
            }
        }
    }

This environment executes commands in ConTree sandboxes using ConTree SDK

Setup

  1. Install the dependencies:

    pip install "mini-swe-agent[contree]"
    

  2. Set up ConTree token and base_url:

    export CONTREE_TOKEN="your-contree-token"
    export CONTREE_BASE_URL="your-given-base-url-for-contree"
    

Usage

Run mini-swe-agent like with any other environment:

mini-extra swebench \
    --subset verified \
    --split test \
    --workers 100
    --environment-class contree

It can be specified both through cli parameter or by setting environment_class to contree in your swebench.yaml config