Add optional sandbox_id parameter to start_sandbox method (#12382)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Tim O'Farrell
2026-01-12 15:43:24 -07:00
committed by GitHub
parent 9cf7d64bfe
commit 13762eba7c
9 changed files with 141 additions and 14 deletions

View File

@@ -478,7 +478,15 @@ class LiveStatusAppConversationService(AppConversationServiceBase):
"""Wait for sandbox to start and return info."""
# Get or create the sandbox
if not task.request.sandbox_id:
sandbox = await self.sandbox_service.start_sandbox()
# Convert conversation_id to hex string if present
sandbox_id_str = (
task.request.conversation_id.hex
if task.request.conversation_id is not None
else None
)
sandbox = await self.sandbox_service.start_sandbox(
sandbox_id=sandbox_id_str
)
task.sandbox_id = sandbox.id
else:
sandbox_info = await self.sandbox_service.get_sandbox(

View File

@@ -294,7 +294,9 @@ class DockerSandboxService(SandboxService):
except (NotFound, APIError):
return None
async def start_sandbox(self, sandbox_spec_id: str | None = None) -> SandboxInfo:
async def start_sandbox(
self, sandbox_spec_id: str | None = None, sandbox_id: str | None = None
) -> SandboxInfo:
"""Start a new sandbox."""
# Enforce sandbox limits by cleaning up old sandboxes
await self.pause_old_sandboxes(self.max_num_sandboxes - 1)
@@ -309,10 +311,12 @@ class DockerSandboxService(SandboxService):
raise ValueError('Sandbox Spec not found')
sandbox_spec = sandbox_spec_maybe
# Generate container ID and session api key
container_name = (
f'{self.container_name_prefix}{base62.encodebytes(os.urandom(16))}'
)
# Generate a sandbox id if none was provided
if sandbox_id is None:
sandbox_id = base62.encodebytes(os.urandom(16))
# Generate container name and session api key
container_name = f'{self.container_name_prefix}{sandbox_id}'
session_api_key = base62.encodebytes(os.urandom(32))
# Prepare environment variables

View File

@@ -286,7 +286,9 @@ class ProcessSandboxService(SandboxService):
return None
async def start_sandbox(self, sandbox_spec_id: str | None = None) -> SandboxInfo:
async def start_sandbox(
self, sandbox_spec_id: str | None = None, sandbox_id: str | None = None
) -> SandboxInfo:
"""Start a new sandbox."""
# Get sandbox spec
if sandbox_spec_id is None:
@@ -300,7 +302,9 @@ class ProcessSandboxService(SandboxService):
sandbox_spec = sandbox_spec_maybe
# Generate unique sandbox ID and session API key
sandbox_id = base62.encodebytes(os.urandom(16))
# Use provided sandbox_id if available, otherwise generate a random one
if sandbox_id is None:
sandbox_id = base62.encodebytes(os.urandom(16))
session_api_key = base62.encodebytes(os.urandom(32))
# Find available port

View File

@@ -383,7 +383,9 @@ class RemoteSandboxService(SandboxService):
return None
async def start_sandbox(self, sandbox_spec_id: str | None = None) -> SandboxInfo:
async def start_sandbox(
self, sandbox_spec_id: str | None = None, sandbox_id: str | None = None
) -> SandboxInfo:
"""Start a new sandbox by creating a remote runtime."""
try:
# Enforce sandbox limits by cleaning up old sandboxes
@@ -402,8 +404,9 @@ class RemoteSandboxService(SandboxService):
raise ValueError('Sandbox Spec not found')
sandbox_spec = sandbox_spec_maybe
# Create a unique id
sandbox_id = base62.encodebytes(os.urandom(16))
# Create a unique id, use provided sandbox_id if available
if sandbox_id is None:
sandbox_id = base62.encodebytes(os.urandom(16))
# get user id
user_id = await self.user_context.get_user_id()

View File

@@ -50,10 +50,14 @@ class SandboxService(ABC):
return results
@abstractmethod
async def start_sandbox(self, sandbox_spec_id: str | None = None) -> SandboxInfo:
async def start_sandbox(
self, sandbox_spec_id: str | None = None, sandbox_id: str | None = None
) -> SandboxInfo:
"""Begin the process of starting a sandbox.
Return the info on the new sandbox. If no spec is selected, use the default.
If sandbox_id is provided, it will be used as the sandbox identifier instead
of generating a random one.
"""
@abstractmethod