mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-10 15:28:14 -05:00
Add VSCode URL support and worker ports to sandbox services (#11426)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -19,6 +19,8 @@ from openhands.app_server.sandbox.docker_sandbox_spec_service import get_docker_
|
||||
from openhands.app_server.sandbox.sandbox_models import (
|
||||
AGENT_SERVER,
|
||||
VSCODE,
|
||||
WORKER_1,
|
||||
WORKER_2,
|
||||
ExposedUrl,
|
||||
SandboxInfo,
|
||||
SandboxPage,
|
||||
@@ -124,6 +126,10 @@ class DockerSandboxService(SandboxService):
|
||||
session_api_key = None
|
||||
|
||||
if status == SandboxStatus.RUNNING:
|
||||
# Get session API key first
|
||||
env = self._get_container_env_vars(container)
|
||||
session_api_key = env.get(SESSION_API_KEY_VARIABLE)
|
||||
|
||||
# Get the first exposed port mapping
|
||||
exposed_urls = []
|
||||
port_bindings = container.attrs.get('NetworkSettings', {}).get('Ports', {})
|
||||
@@ -141,19 +147,19 @@ class DockerSandboxService(SandboxService):
|
||||
None,
|
||||
)
|
||||
if exposed_port:
|
||||
url = self.container_url_pattern.format(port=host_port)
|
||||
|
||||
# VSCode URLs require the api_key and working dir
|
||||
if exposed_port.name == VSCODE:
|
||||
url += f'/?tkn={session_api_key}&folder={container.attrs["Config"]["WorkingDir"]}'
|
||||
|
||||
exposed_urls.append(
|
||||
ExposedUrl(
|
||||
name=exposed_port.name,
|
||||
url=self.container_url_pattern.format(
|
||||
port=host_port
|
||||
),
|
||||
url=url,
|
||||
)
|
||||
)
|
||||
|
||||
# Get session API key
|
||||
env = self._get_container_env_vars(container)
|
||||
session_api_key = env[SESSION_API_KEY_VARIABLE]
|
||||
|
||||
return SandboxInfo(
|
||||
id=container.name,
|
||||
created_by_user_id=None,
|
||||
@@ -394,6 +400,20 @@ class DockerSandboxServiceInjector(SandboxServiceInjector):
|
||||
),
|
||||
container_port=8001,
|
||||
),
|
||||
ExposedPort(
|
||||
name=WORKER_1,
|
||||
description=(
|
||||
'The first port on which the agent should start application servers.'
|
||||
),
|
||||
container_port=8011,
|
||||
),
|
||||
ExposedPort(
|
||||
name=WORKER_2,
|
||||
description=(
|
||||
'The first port on which the agent should start application servers.'
|
||||
),
|
||||
container_port=8012,
|
||||
),
|
||||
]
|
||||
)
|
||||
health_check_path: str | None = Field(
|
||||
|
||||
@@ -14,7 +14,7 @@ from openhands.app_server.sandbox.sandbox_spec_models import (
|
||||
SandboxSpecInfo,
|
||||
)
|
||||
from openhands.app_server.sandbox.sandbox_spec_service import (
|
||||
AGENT_SERVER_VERSION,
|
||||
AGENT_SERVER_IMAGE,
|
||||
SandboxSpecService,
|
||||
SandboxSpecServiceInjector,
|
||||
)
|
||||
@@ -34,7 +34,7 @@ def get_docker_client() -> docker.DockerClient:
|
||||
def get_default_sandbox_specs():
|
||||
return [
|
||||
SandboxSpecInfo(
|
||||
id=f'ghcr.io/all-hands-ai/agent-server:{AGENT_SERVER_VERSION[:7]}-python',
|
||||
id=AGENT_SERVER_IMAGE,
|
||||
command=['--port', '8000'],
|
||||
initial_env={
|
||||
'OPENVSCODE_SERVER_ROOT': '/openhands/.openvscode-server',
|
||||
|
||||
@@ -10,7 +10,7 @@ from openhands.app_server.sandbox.sandbox_spec_models import (
|
||||
SandboxSpecInfo,
|
||||
)
|
||||
from openhands.app_server.sandbox.sandbox_spec_service import (
|
||||
AGENT_SERVER_VERSION,
|
||||
AGENT_SERVER_IMAGE,
|
||||
SandboxSpecService,
|
||||
SandboxSpecServiceInjector,
|
||||
)
|
||||
@@ -20,7 +20,7 @@ from openhands.app_server.services.injector import InjectorState
|
||||
def get_default_sandbox_specs():
|
||||
return [
|
||||
SandboxSpecInfo(
|
||||
id=AGENT_SERVER_VERSION,
|
||||
id=AGENT_SERVER_IMAGE,
|
||||
command=['python', '-m', 'openhands.agent_server'],
|
||||
initial_env={
|
||||
# VSCode disabled for now
|
||||
|
||||
@@ -26,6 +26,9 @@ from openhands.app_server.event_callback.event_callback_service import (
|
||||
)
|
||||
from openhands.app_server.sandbox.sandbox_models import (
|
||||
AGENT_SERVER,
|
||||
VSCODE,
|
||||
WORKER_1,
|
||||
WORKER_2,
|
||||
ExposedUrl,
|
||||
SandboxInfo,
|
||||
SandboxPage,
|
||||
@@ -144,6 +147,17 @@ class RemoteSandboxService(SandboxService):
|
||||
url = runtime.get('url', None)
|
||||
if url:
|
||||
exposed_urls.append(ExposedUrl(name=AGENT_SERVER, url=url))
|
||||
vscode_url = (
|
||||
_build_service_url(url, 'vscode')
|
||||
+ f'/?tkn={session_api_key}&folder={runtime["working_dir"]}'
|
||||
)
|
||||
exposed_urls.append(ExposedUrl(name=VSCODE, url=vscode_url))
|
||||
exposed_urls.append(
|
||||
ExposedUrl(name=WORKER_1, url=_build_service_url(url, 'work-1'))
|
||||
)
|
||||
exposed_urls.append(
|
||||
ExposedUrl(name=WORKER_2, url=_build_service_url(url, 'work-2'))
|
||||
)
|
||||
else:
|
||||
exposed_urls = None
|
||||
else:
|
||||
@@ -383,6 +397,11 @@ class RemoteSandboxService(SandboxService):
|
||||
return False
|
||||
|
||||
|
||||
def _build_service_url(url: str, service_name: str):
|
||||
scheme, host_and_path = url.split('://')
|
||||
return scheme + '://' + service_name + '-' + host_and_path
|
||||
|
||||
|
||||
async def poll_agent_servers(api_url: str, api_key: str, sleep_interval: int):
|
||||
"""When the app server does not have a public facing url, we poll the agent
|
||||
servers for the most recent data.
|
||||
|
||||
@@ -10,7 +10,7 @@ from openhands.app_server.sandbox.sandbox_spec_models import (
|
||||
SandboxSpecInfo,
|
||||
)
|
||||
from openhands.app_server.sandbox.sandbox_spec_service import (
|
||||
AGENT_SERVER_VERSION,
|
||||
AGENT_SERVER_IMAGE,
|
||||
SandboxSpecService,
|
||||
SandboxSpecServiceInjector,
|
||||
)
|
||||
@@ -20,7 +20,7 @@ from openhands.app_server.services.injector import InjectorState
|
||||
def get_default_sandbox_specs():
|
||||
return [
|
||||
SandboxSpecInfo(
|
||||
id=f'ghcr.io/all-hands-ai/agent-server:{AGENT_SERVER_VERSION[:7]}-python',
|
||||
id=AGENT_SERVER_IMAGE,
|
||||
command=['/usr/local/bin/openhands-agent-server', '--port', '60000'],
|
||||
initial_env={
|
||||
'OPENVSCODE_SERVER_ROOT': '/openhands/.openvscode-server',
|
||||
@@ -28,6 +28,7 @@ def get_default_sandbox_specs():
|
||||
'OH_ENABLE_VNC': '0',
|
||||
'OH_CONVERSATIONS_PATH': '/workspace/conversations',
|
||||
'OH_BASH_EVENTS_DIR': '/workspace/bash_events',
|
||||
'OH_VSCODE_PORT': '60001',
|
||||
},
|
||||
working_dir='/workspace/projects',
|
||||
)
|
||||
|
||||
@@ -25,6 +25,8 @@ class ExposedUrl(BaseModel):
|
||||
# Standard names
|
||||
AGENT_SERVER = 'AGENT_SERVER'
|
||||
VSCODE = 'VSCODE'
|
||||
WORKER_1 = 'WORKER_1'
|
||||
WORKER_2 = 'WORKER_2'
|
||||
|
||||
|
||||
class SandboxInfo(BaseModel):
|
||||
|
||||
@@ -11,7 +11,7 @@ from openhands.sdk.utils.models import DiscriminatedUnionMixin
|
||||
|
||||
# The version of the agent server to use for deployments.
|
||||
# Typically this will be the same as the values from the pyproject.toml
|
||||
AGENT_SERVER_VERSION = '08cf609a996523c0199c61c768d74417b7e96109'
|
||||
AGENT_SERVER_IMAGE = 'ghcr.io/all-hands-ai/agent-server:ab36fd6-python'
|
||||
|
||||
|
||||
class SandboxSpecService(ABC):
|
||||
|
||||
Reference in New Issue
Block a user