Skip browser dependency build in Dockerfile when browser is disabled (#9815)

This commit is contained in:
Boxuan Li
2025-07-21 08:34:11 -07:00
committed by GitHub
parent df75116184
commit 19ca52f954
11 changed files with 97 additions and 18 deletions

View File

@@ -212,7 +212,7 @@ def _load_runtime(
runtime_startup_env_vars: dict[str, str] | None = None,
docker_runtime_kwargs: dict[str, str] | None = None,
override_mcp_config: MCPConfig | None = None,
enable_browser: bool = True,
enable_browser: bool = False,
) -> tuple[Runtime, OpenHandsConfig]:
sid = 'rt_' + str(random.randint(100000, 999999))

View File

@@ -38,7 +38,9 @@ def test_view_file(temp_dir, runtime_cls, run_as_openhands):
def test_view_directory(temp_dir, runtime_cls, run_as_openhands):
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create test file
test_file = os.path.join(config.workspace_mount_path_in_sandbox, 'test.txt')

View File

@@ -36,6 +36,7 @@ def test_browsergym_eval_env(runtime_cls, temp_dir):
base_container_image='xingyaoww/od-eval-miniwob:v1.0',
browsergym_eval_env='browsergym/miniwob.choose-list',
force_rebuild_runtime=True,
enable_browser=True,
)
from openhands.runtime.browser.browser_env import (
BROWSER_EVAL_GET_GOAL_ACTION,

View File

@@ -144,7 +144,9 @@ def test_browser_disabled(temp_dir, runtime_cls, run_as_openhands):
def test_simple_browse(temp_dir, runtime_cls, run_as_openhands):
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
# Test browse
action_cmd = CmdRunAction(command='python3 -m http.server 8000 > server.log 2>&1 &')
@@ -189,7 +191,9 @@ def test_simple_browse(temp_dir, runtime_cls, run_as_openhands):
def test_browser_navigation_actions(temp_dir, runtime_cls, run_as_openhands):
"""Test browser navigation actions: goto, go_back, go_forward, noop."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create test HTML pages
page1_content = """
@@ -322,7 +326,9 @@ def test_browser_navigation_actions(temp_dir, runtime_cls, run_as_openhands):
def test_browser_form_interactions(temp_dir, runtime_cls, run_as_openhands):
"""Test browser form interaction actions: fill, click, select_option, clear."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create a test form page
form_content = """
@@ -536,7 +542,9 @@ fill("{textarea_bid}", "This is a test message")
def test_browser_interactive_actions(temp_dir, runtime_cls, run_as_openhands):
"""Test browser interactive actions: scroll, hover, fill, press, focus."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create a test page with scrollable content
scroll_content = """
@@ -742,7 +750,9 @@ scroll(0, 400)
def test_browser_file_upload(temp_dir, runtime_cls, run_as_openhands):
"""Test browser file upload action."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create a test file to upload
test_file_content = 'This is a test file for upload testing.'
@@ -897,7 +907,9 @@ def test_browser_file_upload(temp_dir, runtime_cls, run_as_openhands):
def test_read_pdf_browse(temp_dir, runtime_cls, run_as_openhands):
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create a PDF file using reportlab in the host environment
from reportlab.lib.pagesizes import letter
@@ -969,7 +981,9 @@ def test_read_pdf_browse(temp_dir, runtime_cls, run_as_openhands):
def test_read_png_browse(temp_dir, runtime_cls, run_as_openhands):
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Create a PNG file using PIL in the host environment
from PIL import Image, ImageDraw
@@ -1037,7 +1051,9 @@ def test_read_png_browse(temp_dir, runtime_cls, run_as_openhands):
def test_download_file(temp_dir, runtime_cls, run_as_openhands):
"""Test downloading a file using the browser."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, enable_browser=True
)
try:
# Minimal PDF content for testing
pdf_content = b"""%PDF-1.4

View File

@@ -128,7 +128,11 @@ async def test_fetch_mcp_via_stdio(temp_dir, runtime_cls, run_as_openhands):
)
override_mcp_config = MCPConfig(stdio_servers=[mcp_stdio_server_config])
runtime, config = _load_runtime(
temp_dir, runtime_cls, run_as_openhands, override_mcp_config=override_mcp_config
temp_dir,
runtime_cls,
run_as_openhands,
override_mcp_config=override_mcp_config,
enable_browser=True,
)
# Test browser server
@@ -220,6 +224,7 @@ async def test_both_stdio_and_sse_mcp(
runtime_cls,
run_as_openhands,
override_mcp_config=override_mcp_config,
enable_browser=True,
)
# ======= Test SSE server =======
@@ -297,6 +302,7 @@ async def test_microagent_and_one_stdio_mcp_in_config(
runtime_cls,
run_as_openhands,
override_mcp_config=override_mcp_config,
enable_browser=True,
)
# NOTE: this simulate the case where the microagent adds a new stdio server to the runtime

View File

@@ -101,7 +101,7 @@ def test_prep_build_folder(temp_dir):
def test_get_hash_for_lock_files():
with patch('builtins.open', mock_open(read_data='mock-data'.encode())):
hash = get_hash_for_lock_files('some_base_image')
hash = get_hash_for_lock_files('some_base_image', enable_browser=True)
# Since we mocked open to always return "mock_data", the hash is the result
# of hashing the name of the base image followed by "mock-data" twice
md5 = hashlib.md5()
@@ -111,6 +111,31 @@ def test_get_hash_for_lock_files():
assert hash == truncate_hash(md5.hexdigest())
def test_get_hash_for_lock_files_different_enable_browser():
with patch('builtins.open', mock_open(read_data='mock-data'.encode())):
hash_true = get_hash_for_lock_files('some_base_image', enable_browser=True)
hash_false = get_hash_for_lock_files('some_base_image', enable_browser=False)
# Hash with enable_browser=True should not include the enable_browser value
md5_true = hashlib.md5()
md5_true.update('some_base_image'.encode())
for _ in range(2):
md5_true.update('mock-data'.encode())
expected_hash_true = truncate_hash(md5_true.hexdigest())
# Hash with enable_browser=False should include the enable_browser value
md5_false = hashlib.md5()
md5_false.update('some_base_image'.encode())
md5_false.update('False'.encode()) # enable_browser=False is included
for _ in range(2):
md5_false.update('mock-data'.encode())
expected_hash_false = truncate_hash(md5_false.hexdigest())
assert hash_true == expected_hash_true
assert hash_false == expected_hash_false
assert hash_true != hash_false # They should be different
def test_get_hash_for_source_files():
dirhash_mock = MagicMock()
dirhash_mock.return_value = '1f69bd20d68d9e3874d5bf7f7459709b'
@@ -247,7 +272,7 @@ def test_build_runtime_image_from_scratch():
== f'{get_runtime_image_repo()}:{OH_VERSION}_mock-lock-tag_mock-source-tag'
)
mock_prep_build_folder.assert_called_once_with(
ANY, base_image, BuildFromImageType.SCRATCH, None
ANY, base_image, BuildFromImageType.SCRATCH, None, True
)
@@ -342,6 +367,7 @@ def test_build_runtime_image_exact_hash_not_exist_and_lock_exist():
f'{get_runtime_image_repo()}:{OH_VERSION}_mock-lock-tag',
BuildFromImageType.LOCK,
None,
True,
)
@@ -401,6 +427,7 @@ def test_build_runtime_image_exact_hash_not_exist_and_lock_not_exist_and_version
f'{get_runtime_image_repo()}:{OH_VERSION}_mock-versioned-tag',
BuildFromImageType.VERSIONED,
None,
True,
)