From 2697d82286b694ab1e0e136dc3775a3f16a5250d Mon Sep 17 00:00:00 2001 From: youhuanghe <1051233107@qq.com> Date: Thu, 9 Apr 2026 12:14:23 +0000 Subject: [PATCH] refactor(box): run Box Runtime as subprocess inside LangBot container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the separate langbot_box_runtime Docker service. Box Runtime now always launches as a local stdio subprocess, regardless of whether LangBot runs in Docker or not. The WebSocket transport path is kept only for explicit runtime_url configuration (remote deployment). This simplifies deployment by eliminating cross-container path mapping and network hops. Box Runtime is a pure scheduling process (talks to Docker socket / nsjail), it does not execute user code or touch the filesystem, so container isolation is unnecessary — unlike Plugin Runtime. --- docker/docker-compose.yaml | 22 ++++------------------ src/langbot/pkg/box/connector.py | 5 +---- src/langbot/templates/config.yaml | 2 +- tests/unit_tests/box/test_box_connector.py | 18 ++++++------------ 4 files changed, 12 insertions(+), 35 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index e74e5d62..321d1594 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -4,24 +4,6 @@ version: "3" services: - langbot_box_runtime: - image: rockchin/langbot:latest - container_name: langbot_box_runtime - command: ["uv", "run", "--no-sync", "-m", "langbot_plugin.box.server"] - volumes: - # Mount the container runtime socket from the host. - # Uncomment the one that matches your container runtime: - # - /var/run/podman/podman.sock:/var/run/podman/podman.sock # Podman - - /var/run/docker.sock:/var/run/docker.sock # Docker - - ./data/box:/workspaces - ports: - - 5410:5410 - restart: on-failure - environment: - - TZ=Asia/Shanghai - networks: - - langbot_network - langbot_plugin_runtime: image: rockchin/langbot:latest container_name: langbot_plugin_runtime @@ -42,6 +24,10 @@ services: volumes: - ./data:/app/data - ./data/box:/workspaces + # Mount container runtime socket for Box sandbox (Docker backend). + # Uncomment the one that matches your container runtime: + # - /var/run/podman/podman.sock:/var/run/podman/podman.sock # Podman + - /var/run/docker.sock:/var/run/docker.sock # Docker restart: on-failure environment: - TZ=Asia/Shanghai diff --git a/src/langbot/pkg/box/connector.py b/src/langbot/pkg/box/connector.py index 557a85b4..83e51392 100644 --- a/src/langbot/pkg/box/connector.py +++ b/src/langbot/pkg/box/connector.py @@ -12,7 +12,6 @@ from langbot_plugin.runtime.io.connection import Connection from langbot_plugin.box.client import ActionRPCBoxClient from langbot_plugin.box.errors import BoxRuntimeUnavailableError -from ..utils import platform if TYPE_CHECKING: from ..core import app as core_app @@ -31,8 +30,6 @@ def resolve_box_ws_relay_url(ap: core_app.Application) -> str: if runtime_url: return runtime_url - if platform.get_platform() == 'docker': - return 'http://langbot_box_runtime:5410' return 'http://127.0.0.1:5410' @@ -160,4 +157,4 @@ class BoxRuntimeConnector: return str(_get_box_config(self.ap).get('runtime_url', '')).strip() def _should_manage_local_runtime(self) -> bool: - return not self.configured_runtime_url and platform.get_platform() != 'docker' + return not self.configured_runtime_url diff --git a/src/langbot/templates/config.yaml b/src/langbot/templates/config.yaml index c8e927bb..2da82b40 100644 --- a/src/langbot/templates/config.yaml +++ b/src/langbot/templates/config.yaml @@ -89,7 +89,7 @@ monitoring: check_interval_hours: 1 box: profile: 'default' - runtime_url: '' # Leave empty to use defaults: http://127.0.0.1:5410 locally, http://langbot_box_runtime:5410 in Docker + runtime_url: '' # Leave empty to auto-launch local Box Runtime subprocess. Set to a URL only for remote/external deployment. shared_host_root: './data/box' # For Docker deployment, use '/workspaces' default_host_workspace: '' # Defaults to '/default' allowed_host_mount_roots: # Defaults to [''] when left empty diff --git a/tests/unit_tests/box/test_box_connector.py b/tests/unit_tests/box/test_box_connector.py index 7d098912..1664bb05 100644 --- a/tests/unit_tests/box/test_box_connector.py +++ b/tests/unit_tests/box/test_box_connector.py @@ -25,12 +25,7 @@ def make_app(logger: Mock, runtime_url: str = ''): ) -def patch_platform(monkeypatch: pytest.MonkeyPatch, value: str): - monkeypatch.setattr('langbot.pkg.box.connector.platform.get_platform', lambda: value) - - -def test_box_runtime_connector_manages_local_when_no_url(monkeypatch: pytest.MonkeyPatch): - patch_platform(monkeypatch, 'linux') +def test_box_runtime_connector_manages_local_when_no_url(): connector = BoxRuntimeConnector(make_app(Mock())) assert connector.manages_local_runtime is True @@ -45,16 +40,15 @@ def test_box_runtime_connector_remote_when_url_configured(): assert isinstance(connector.client, ActionRPCBoxClient) -def test_box_runtime_connector_remote_when_docker(monkeypatch: pytest.MonkeyPatch): - patch_platform(monkeypatch, 'docker') +def test_box_runtime_connector_manages_local_in_docker(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr('langbot.pkg.utils.platform.get_platform', lambda: 'docker') connector = BoxRuntimeConnector(make_app(Mock())) - assert connector.manages_local_runtime is False - assert connector.ws_relay_base_url == 'http://langbot_box_runtime:5410' + assert connector.manages_local_runtime is True + assert connector.ws_relay_base_url == 'http://127.0.0.1:5410' -def test_box_runtime_connector_ws_relay_url_default(monkeypatch: pytest.MonkeyPatch): - patch_platform(monkeypatch, 'linux') +def test_box_runtime_connector_ws_relay_url_default(): connector = BoxRuntimeConnector(make_app(Mock())) assert connector.ws_relay_base_url == 'http://127.0.0.1:5410'