diff --git a/config.template.toml b/config.template.toml index 9d84c5a7ff..6f626e6bee 100644 --- a/config.template.toml +++ b/config.template.toml @@ -217,6 +217,9 @@ llm_config = 'gpt3' # Use host network #use_host_network = false +# runtime extra build args +#runtime_extra_build_args = ["--network=host", "--add-host=host.docker.internal:host-gateway"] + # Enable auto linting after editing #enable_auto_lint = false diff --git a/openhands/core/config/sandbox_config.py b/openhands/core/config/sandbox_config.py index fee44cbaff..c636706755 100644 --- a/openhands/core/config/sandbox_config.py +++ b/openhands/core/config/sandbox_config.py @@ -48,6 +48,7 @@ class SandboxConfig: False # once enabled, OpenHands would lint files after editing ) use_host_network: bool = False + runtime_extra_build_args: list[str] | None = None initialize_plugins: bool = True force_rebuild_runtime: bool = False runtime_extra_deps: str | None = None diff --git a/openhands/runtime/builder/base.py b/openhands/runtime/builder/base.py index df2ee99035..acfe3c60fb 100644 --- a/openhands/runtime/builder/base.py +++ b/openhands/runtime/builder/base.py @@ -8,6 +8,7 @@ class RuntimeBuilder(abc.ABC): path: str, tags: list[str], platform: str | None = None, + extra_build_args: list[str] | None = None, ) -> str: """Build the runtime image. @@ -15,6 +16,7 @@ class RuntimeBuilder(abc.ABC): path (str): The path to the runtime image's build directory. tags (list[str]): The tags to apply to the runtime image (e.g., ["repo:my-repo", "sha:my-sha"]). platform (str, optional): The target platform for the build. Defaults to None. + extra_build_args (list[str], optional): Additional build arguments to pass to the builder. Defaults to None. Returns: str: The name:tag of the runtime image after build (e.g., "repo:sha"). diff --git a/openhands/runtime/builder/docker.py b/openhands/runtime/builder/docker.py index 9cdf0f998f..880b1c73c5 100644 --- a/openhands/runtime/builder/docker.py +++ b/openhands/runtime/builder/docker.py @@ -28,8 +28,8 @@ class DockerRuntimeBuilder(RuntimeBuilder): path: str, tags: list[str], platform: str | None = None, - use_local_cache: bool = False, extra_build_args: list[str] | None = None, + use_local_cache: bool = False, ) -> str: """Builds a Docker image using BuildKit and handles the build logs appropriately. diff --git a/openhands/runtime/builder/remote.py b/openhands/runtime/builder/remote.py index c9d3228a70..5cfe1a4943 100644 --- a/openhands/runtime/builder/remote.py +++ b/openhands/runtime/builder/remote.py @@ -23,7 +23,13 @@ class RemoteRuntimeBuilder(RuntimeBuilder): self.session = requests.Session() self.session.headers.update({'X-API-Key': self.api_key}) - def build(self, path: str, tags: list[str], platform: str | None = None) -> str: + def build( + self, + path: str, + tags: list[str], + platform: str | None = None, + extra_build_args: list[str] | None = None, + ) -> str: """Builds a Docker image using the Runtime API's /build endpoint.""" # Create a tar archive of the build context tar_buffer = io.BytesIO() diff --git a/openhands/runtime/impl/eventstream/eventstream_runtime.py b/openhands/runtime/impl/eventstream/eventstream_runtime.py index 8cedcbe546..2dc99aa2aa 100644 --- a/openhands/runtime/impl/eventstream/eventstream_runtime.py +++ b/openhands/runtime/impl/eventstream/eventstream_runtime.py @@ -229,6 +229,7 @@ class EventStreamRuntime(Runtime): platform=self.config.sandbox.platform, extra_deps=self.config.sandbox.runtime_extra_deps, force_rebuild=self.config.sandbox.force_rebuild_runtime, + extra_build_args=self.config.sandbox.runtime_extra_build_args, ) self.log( diff --git a/openhands/runtime/utils/runtime_build.py b/openhands/runtime/utils/runtime_build.py index eab98befe5..de939efd9a 100644 --- a/openhands/runtime/utils/runtime_build.py +++ b/openhands/runtime/utils/runtime_build.py @@ -111,6 +111,7 @@ def build_runtime_image( build_folder: str | None = None, dry_run: bool = False, force_rebuild: bool = False, + extra_build_args: List[str] | None = None, ) -> str: """Prepares the final docker build folder. If dry_run is False, it will also build the OpenHands runtime Docker image using the docker build folder. @@ -123,6 +124,7 @@ def build_runtime_image( - build_folder (str): The directory to use for the build. If not provided a temporary directory will be used - dry_run (bool): if True, it will only ready the build folder. It will not actually build the Docker image - force_rebuild (bool): if True, it will create the Dockerfile which uses the base_image + - extra_build_args (List[str]): Additional build arguments to pass to the builder Returns: - str: :. Where MD5 hash is the hash of the docker build folder @@ -139,6 +141,7 @@ def build_runtime_image( dry_run=dry_run, force_rebuild=force_rebuild, platform=platform, + extra_build_args=extra_build_args, ) return result @@ -150,6 +153,7 @@ def build_runtime_image( dry_run=dry_run, force_rebuild=force_rebuild, platform=platform, + extra_build_args=extra_build_args, ) return result @@ -162,6 +166,7 @@ def build_runtime_image_in_folder( dry_run: bool, force_rebuild: bool, platform: str | None = None, + extra_build_args: List[str] | None = None, ) -> str: runtime_image_repo, _ = get_runtime_image_repo_and_tag(base_image) lock_tag = f'oh_v{oh_version}_{get_hash_for_lock_files(base_image)}' @@ -193,6 +198,7 @@ def build_runtime_image_in_folder( lock_tag, versioned_tag, platform, + extra_build_args=extra_build_args, ) return hash_image_name @@ -234,6 +240,7 @@ def build_runtime_image_in_folder( if build_from == BuildFromImageType.SCRATCH else None, platform=platform, + extra_build_args=extra_build_args, ) return hash_image_name @@ -339,6 +346,7 @@ def _build_sandbox_image( lock_tag: str, versioned_tag: str | None, platform: str | None = None, + extra_build_args: List[str] | None = None, ): """Build and tag the sandbox image. The image will be tagged with all tags that do not yet exist""" names = [ @@ -350,7 +358,10 @@ def _build_sandbox_image( names = [name for name in names if not runtime_builder.image_exists(name, False)] image_name = runtime_builder.build( - path=str(build_folder), tags=names, platform=platform + path=str(build_folder), + tags=names, + platform=platform, + extra_build_args=extra_build_args, ) if not image_name: raise RuntimeError(f'Build failed for image {names}') diff --git a/tests/unit/test_runtime_build.py b/tests/unit/test_runtime_build.py index 79a7c9a22b..b5cbd91056 100644 --- a/tests/unit/test_runtime_build.py +++ b/tests/unit/test_runtime_build.py @@ -239,6 +239,7 @@ def test_build_runtime_image_from_scratch(): f'{get_runtime_image_repo()}:{OH_VERSION}_mock-versioned-tag', ], platform=None, + extra_build_args=None, ) assert ( image_name @@ -333,6 +334,7 @@ def test_build_runtime_image_exact_hash_not_exist_and_lock_exist(): # VERSION tag will NOT be included except from scratch ], platform=None, + extra_build_args=None, ) mock_prep_build_folder.assert_called_once_with( ANY, @@ -391,6 +393,7 @@ def test_build_runtime_image_exact_hash_not_exist_and_lock_not_exist_and_version # VERSION tag will NOT be included except from scratch ], platform=None, + extra_build_args=None, ) mock_prep_build_folder.assert_called_once_with( ANY,