Fixes to unblock frontend (#11488)

Co-authored-by: Ray Myers <ray.myers@gmail.com>
This commit is contained in:
Tim O'Farrell
2025-10-23 14:43:45 -06:00
committed by GitHub
parent eb954164a5
commit 4b303ec9b4
18 changed files with 56 additions and 46 deletions

View File

@@ -46,7 +46,7 @@ jobs:
else else
json=$(jq -n -c '[ json=$(jq -n -c '[
{ image: "nikolaik/python-nodejs:python3.12-nodejs22", tag: "nikolaik" }, { image: "nikolaik/python-nodejs:python3.12-nodejs22", tag: "nikolaik" },
{ image: "ghcr.io/all-hands-ai/python-nodejs:python3.13-nodejs22-trixie", tag: "trixie" }, { image: "ghcr.io/openhands/python-nodejs:python3.13-nodejs22-trixie", tag: "trixie" },
{ image: "ubuntu:24.04", tag: "ubuntu" } { image: "ubuntu:24.04", tag: "ubuntu" }
]') ]')
fi fi

View File

@@ -159,7 +159,7 @@ poetry run pytest ./tests/unit/test_*.py
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image. container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.59-nikolaik` Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:0.59-nikolaik`
## Develop inside Docker container ## Develop inside Docker container

View File

@@ -12,7 +12,7 @@ services:
- SANDBOX_API_HOSTNAME=host.docker.internal - SANDBOX_API_HOSTNAME=host.docker.internal
- DOCKER_HOST_ADDR=host.docker.internal - DOCKER_HOST_ADDR=host.docker.internal
# #
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.59-nikolaik} - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/openhands/runtime:0.59-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} - SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace} - WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports: ports:

View File

@@ -1,5 +1,5 @@
ARG OPENHANDS_VERSION=latest ARG OPENHANDS_VERSION=latest
ARG BASE="ghcr.io/all-hands-ai/openhands" ARG BASE="ghcr.io/openhands/openhands"
FROM ${BASE}:${OPENHANDS_VERSION} FROM ${BASE}:${OPENHANDS_VERSION}
# Datadog labels # Datadog labels

View File

@@ -64,7 +64,7 @@ python enterprise_local/convert_to_env.py
You'll also need to set up the runtime image, so that the dev server doesn't try to rebuild it. You'll also need to set up the runtime image, so that the dev server doesn't try to rebuild it.
``` ```
export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:main-nikolaik export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:main-nikolaik
docker pull $SANDBOX_RUNTIME_CONTAINER_IMAGE docker pull $SANDBOX_RUNTIME_CONTAINER_IMAGE
``` ```
@@ -203,7 +203,7 @@ And then invoking `printenv`. NOTE: _DO NOT DO THIS WITH PROD!!!_ (Hopefully by
"REDIS_HOST": "localhost:6379", "REDIS_HOST": "localhost:6379",
"OPENHANDS": "<YOUR LOCAL OSS OPENHANDS DIR>", "OPENHANDS": "<YOUR LOCAL OSS OPENHANDS DIR>",
"FRONTEND_DIRECTORY": "<YOUR LOCAL OSS OPENHANDS DIR>/frontend/build", "FRONTEND_DIRECTORY": "<YOUR LOCAL OSS OPENHANDS DIR>/frontend/build",
"SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/all-hands-ai/runtime:main-nikolaik", "SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/openhands/runtime:main-nikolaik",
"FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state", "FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state",
"OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig", "OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig",
"GITHUB_APP_ID": "1062351", "GITHUB_APP_ID": "1062351",
@@ -237,7 +237,7 @@ And then invoking `printenv`. NOTE: _DO NOT DO THIS WITH PROD!!!_ (Hopefully by
"REDIS_HOST": "localhost:6379", "REDIS_HOST": "localhost:6379",
"OPENHANDS": "<YOUR LOCAL OSS OPENHANDS DIR>", "OPENHANDS": "<YOUR LOCAL OSS OPENHANDS DIR>",
"FRONTEND_DIRECTORY": "<YOUR LOCAL OSS OPENHANDS DIR>/frontend/build", "FRONTEND_DIRECTORY": "<YOUR LOCAL OSS OPENHANDS DIR>/frontend/build",
"SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/all-hands-ai/runtime:main-nikolaik", "SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/openhands/runtime:main-nikolaik",
"FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state", "FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state",
"OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig", "OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig",
"GITHUB_APP_ID": "1062351", "GITHUB_APP_ID": "1062351",

View File

@@ -12,7 +12,7 @@ git clone -b $OH_SWE_BENCH_REPO_BRANCH $OH_SWE_BENCH_REPO_PATH $EVAL_WORKSPACE/O
# 2. Prepare DATA # 2. Prepare DATA
echo "==== Prepare SWE-bench data ====" echo "==== Prepare SWE-bench data ===="
EVAL_IMAGE=ghcr.io/all-hands-ai/eval-swe-bench:builder_with_conda EVAL_IMAGE=ghcr.io/openhands/eval-swe-bench:builder_with_conda
EVAL_WORKSPACE=$(realpath $EVAL_WORKSPACE) EVAL_WORKSPACE=$(realpath $EVAL_WORKSPACE)
chmod +x $EVAL_WORKSPACE/OH-SWE-bench/swebench/harness/prepare_data.sh chmod +x $EVAL_WORKSPACE/OH-SWE-bench/swebench/harness/prepare_data.sh
if [ -d $EVAL_WORKSPACE/eval_data ]; then if [ -d $EVAL_WORKSPACE/eval_data ]; then

View File

@@ -12,7 +12,7 @@ git clone -b $OH_SWE_BENCH_REPO_BRANCH $OH_SWE_BENCH_REPO_PATH $EVAL_WORKSPACE/O
# 2. Prepare DATA # 2. Prepare DATA
echo "==== Prepare SWE-bench data ====" echo "==== Prepare SWE-bench data ===="
EVAL_IMAGE=ghcr.io/all-hands-ai/eval-swe-bench:builder_with_conda EVAL_IMAGE=ghcr.io/openhands/eval-swe-bench:builder_with_conda
EVAL_WORKSPACE=$(realpath $EVAL_WORKSPACE) EVAL_WORKSPACE=$(realpath $EVAL_WORKSPACE)
chmod +x $EVAL_WORKSPACE/OH-SWE-bench/swebench/harness/prepare_data.sh chmod +x $EVAL_WORKSPACE/OH-SWE-bench/swebench/harness/prepare_data.sh
if [ -d $EVAL_WORKSPACE/eval_data ]; then if [ -d $EVAL_WORKSPACE/eval_data ]; then

View File

@@ -12,7 +12,7 @@ git clone -b $OH_SWE_BENCH_REPO_BRANCH $OH_SWE_BENCH_REPO_PATH $EVAL_WORKSPACE/O
# 2. Prepare DATA # 2. Prepare DATA
echo "==== Prepare SWE-bench data ====" echo "==== Prepare SWE-bench data ===="
EVAL_IMAGE=ghcr.io/all-hands-ai/eval-swe-bench:builder_with_conda EVAL_IMAGE=ghcr.io/openhands/eval-swe-bench:builder_with_conda
EVAL_WORKSPACE=$(realpath $EVAL_WORKSPACE) EVAL_WORKSPACE=$(realpath $EVAL_WORKSPACE)
chmod +x $EVAL_WORKSPACE/OH-SWE-bench/swebench/harness/prepare_data.sh chmod +x $EVAL_WORKSPACE/OH-SWE-bench/swebench/harness/prepare_data.sh
if [ -d $EVAL_WORKSPACE/eval_data ]; then if [ -d $EVAL_WORKSPACE/eval_data ]; then

View File

@@ -162,7 +162,7 @@ while IFS= read -r task_image; do
# Prune unused images and volumes # Prune unused images and volumes
docker image rm "$task_image" docker image rm "$task_image"
docker images "ghcr.io/all-hands-ai/runtime" -q | xargs -r docker rmi -f docker images "ghcr.io/openhands/runtime" -q | xargs -r docker rmi -f
docker volume prune -f docker volume prune -f
docker system prune -f docker system prune -f
done < "$temp_file" done < "$temp_file"

View File

@@ -88,7 +88,10 @@ class AppConversationService(ABC):
@abstractmethod @abstractmethod
async def run_setup_scripts( async def run_setup_scripts(
self, task: AppConversationStartTask, workspace: Workspace self,
task: AppConversationStartTask,
workspace: Workspace,
working_dir: str,
) -> AsyncGenerator[AppConversationStartTask, None]: ) -> AsyncGenerator[AppConversationStartTask, None]:
"""Run the setup scripts for the project and yield status updates""" """Run the setup scripts for the project and yield status updates"""
yield task yield task

View File

@@ -36,23 +36,25 @@ class GitAppConversationService(AppConversationService, ABC):
self, self,
task: AppConversationStartTask, task: AppConversationStartTask,
workspace: AsyncRemoteWorkspace, workspace: AsyncRemoteWorkspace,
working_dir: str,
) -> AsyncGenerator[AppConversationStartTask, None]: ) -> AsyncGenerator[AppConversationStartTask, None]:
task.status = AppConversationStartTaskStatus.PREPARING_REPOSITORY task.status = AppConversationStartTaskStatus.PREPARING_REPOSITORY
yield task yield task
await self.clone_or_init_git_repo(task, workspace) await self.clone_or_init_git_repo(task, workspace, working_dir)
task.status = AppConversationStartTaskStatus.RUNNING_SETUP_SCRIPT task.status = AppConversationStartTaskStatus.RUNNING_SETUP_SCRIPT
yield task yield task
await self.maybe_run_setup_script(workspace) await self.maybe_run_setup_script(workspace, working_dir)
task.status = AppConversationStartTaskStatus.SETTING_UP_GIT_HOOKS task.status = AppConversationStartTaskStatus.SETTING_UP_GIT_HOOKS
yield task yield task
await self.maybe_setup_git_hooks(workspace) await self.maybe_setup_git_hooks(workspace, working_dir)
async def clone_or_init_git_repo( async def clone_or_init_git_repo(
self, self,
task: AppConversationStartTask, task: AppConversationStartTask,
workspace: AsyncRemoteWorkspace, workspace: AsyncRemoteWorkspace,
working_dir: str,
): ):
request = task.request request = task.request
@@ -61,7 +63,7 @@ class GitAppConversationService(AppConversationService, ABC):
_logger.debug('Initializing a new git repository in the workspace.') _logger.debug('Initializing a new git repository in the workspace.')
await workspace.execute_command( await workspace.execute_command(
'git init && git config --global --add safe.directory ' 'git init && git config --global --add safe.directory '
+ workspace.working_dir + working_dir
) )
else: else:
_logger.info('Not initializing a new git repository.') _logger.info('Not initializing a new git repository.')
@@ -77,7 +79,7 @@ class GitAppConversationService(AppConversationService, ABC):
# Clone the repo - this is the slow part! # Clone the repo - this is the slow part!
clone_command = f'git clone {remote_repo_url} {dir_name}' clone_command = f'git clone {remote_repo_url} {dir_name}'
await workspace.execute_command(clone_command, workspace.working_dir) await workspace.execute_command(clone_command, working_dir)
# Checkout the appropriate branch # Checkout the appropriate branch
if request.selected_branch: if request.selected_branch:
@@ -87,14 +89,15 @@ class GitAppConversationService(AppConversationService, ABC):
random_str = base62.encodebytes(os.urandom(16)) random_str = base62.encodebytes(os.urandom(16))
openhands_workspace_branch = f'openhands-workspace-{random_str}' openhands_workspace_branch = f'openhands-workspace-{random_str}'
checkout_command = f'git checkout -b {openhands_workspace_branch}' checkout_command = f'git checkout -b {openhands_workspace_branch}'
await workspace.execute_command(checkout_command, workspace.working_dir) await workspace.execute_command(checkout_command, working_dir)
async def maybe_run_setup_script( async def maybe_run_setup_script(
self, self,
workspace: AsyncRemoteWorkspace, workspace: AsyncRemoteWorkspace,
working_dir: str,
): ):
"""Run .openhands/setup.sh if it exists in the workspace or repository.""" """Run .openhands/setup.sh if it exists in the workspace or repository."""
setup_script = workspace.working_dir + '/.openhands/setup.sh' setup_script = working_dir + '/.openhands/setup.sh'
await workspace.execute_command( await workspace.execute_command(
f'chmod +x {setup_script} && source {setup_script}', timeout=600 f'chmod +x {setup_script} && source {setup_script}', timeout=600
@@ -108,10 +111,11 @@ class GitAppConversationService(AppConversationService, ABC):
async def maybe_setup_git_hooks( async def maybe_setup_git_hooks(
self, self,
workspace: AsyncRemoteWorkspace, workspace: AsyncRemoteWorkspace,
working_dir: str,
): ):
"""Set up git hooks if .openhands/pre-commit.sh exists in the workspace or repository.""" """Set up git hooks if .openhands/pre-commit.sh exists in the workspace or repository."""
command = 'mkdir -p .git/hooks && chmod +x .openhands/pre-commit.sh' command = 'mkdir -p .git/hooks && chmod +x .openhands/pre-commit.sh'
result = await workspace.execute_command(command, workspace.working_dir) result = await workspace.execute_command(command, working_dir)
if result.exit_code: if result.exit_code:
return return
@@ -127,9 +131,7 @@ class GitAppConversationService(AppConversationService, ABC):
f'mv {PRE_COMMIT_HOOK} {PRE_COMMIT_LOCAL} &&' f'mv {PRE_COMMIT_HOOK} {PRE_COMMIT_LOCAL} &&'
f'chmod +x {PRE_COMMIT_LOCAL}' f'chmod +x {PRE_COMMIT_LOCAL}'
) )
result = await workspace.execute_command( result = await workspace.execute_command(command, working_dir)
command, workspace.working_dir
)
if result.exit_code != 0: if result.exit_code != 0:
_logger.error( _logger.error(
f'Failed to preserve existing pre-commit hook: {result.stderr}', f'Failed to preserve existing pre-commit hook: {result.stderr}',

View File

@@ -181,11 +181,11 @@ class LiveStatusAppConversationService(GitAppConversationService):
# Run setup scripts # Run setup scripts
workspace = AsyncRemoteWorkspace( workspace = AsyncRemoteWorkspace(
working_dir=sandbox_spec.working_dir, host=agent_server_url, api_key=sandbox.session_api_key
server_url=agent_server_url,
session_api_key=sandbox.session_api_key,
) )
async for updated_task in self.run_setup_scripts(task, workspace): async for updated_task in self.run_setup_scripts(
task, workspace, sandbox_spec.working_dir
):
yield updated_task yield updated_task
# Build the start request # Build the start request

View File

@@ -90,7 +90,8 @@ class DockerSandboxService(SandboxService):
status_mapping = { status_mapping = {
'running': SandboxStatus.RUNNING, 'running': SandboxStatus.RUNNING,
'paused': SandboxStatus.PAUSED, 'paused': SandboxStatus.PAUSED,
'exited': SandboxStatus.MISSING, # The stop button was pressed in the docker console
'exited': SandboxStatus.PAUSED,
'created': SandboxStatus.STARTING, 'created': SandboxStatus.STARTING,
'restarting': SandboxStatus.STARTING, 'restarting': SandboxStatus.STARTING,
'removing': SandboxStatus.MISSING, 'removing': SandboxStatus.MISSING,

View File

@@ -222,7 +222,7 @@ class IssueResolver:
and not is_experimental and not is_experimental
): ):
runtime_container_image = ( runtime_container_image = (
f'ghcr.io/all-hands-ai/runtime:{openhands.__version__}-nikolaik' f'ghcr.io/openhands/runtime:{openhands.__version__}-nikolaik'
) )
# Convert container image values to string or None # Convert container image values to string or None

View File

@@ -25,7 +25,7 @@ class BuildFromImageType(Enum):
def get_runtime_image_repo() -> str: def get_runtime_image_repo() -> str:
return os.getenv('OH_RUNTIME_RUNTIME_IMAGE_REPO', 'ghcr.io/all-hands-ai/runtime') return os.getenv('OH_RUNTIME_RUNTIME_IMAGE_REPO', 'ghcr.io/openhands/runtime')
def _generate_dockerfile( def _generate_dockerfile(

View File

@@ -1139,25 +1139,29 @@ def _to_conversation_info(app_conversation: AppConversation) -> ConversationInfo
app_conversation.sandbox_status, ConversationStatus.STOPPED app_conversation.sandbox_status, ConversationStatus.STOPPED
) )
runtime_status_mapping = { if conversation_status == ConversationStatus.RUNNING:
AgentExecutionStatus.ERROR: RuntimeStatus.ERROR, runtime_status_mapping = {
AgentExecutionStatus.IDLE: RuntimeStatus.READY, AgentExecutionStatus.ERROR: RuntimeStatus.ERROR,
AgentExecutionStatus.RUNNING: RuntimeStatus.READY, AgentExecutionStatus.IDLE: RuntimeStatus.READY,
AgentExecutionStatus.PAUSED: RuntimeStatus.READY, AgentExecutionStatus.RUNNING: RuntimeStatus.READY,
AgentExecutionStatus.WAITING_FOR_CONFIRMATION: RuntimeStatus.READY, AgentExecutionStatus.PAUSED: RuntimeStatus.READY,
AgentExecutionStatus.FINISHED: RuntimeStatus.READY, AgentExecutionStatus.WAITING_FOR_CONFIRMATION: RuntimeStatus.READY,
AgentExecutionStatus.STUCK: RuntimeStatus.ERROR, AgentExecutionStatus.FINISHED: RuntimeStatus.READY,
} AgentExecutionStatus.STUCK: RuntimeStatus.ERROR,
runtime_status = runtime_status_mapping.get( }
app_conversation.agent_status, RuntimeStatus.ERROR runtime_status = runtime_status_mapping.get(
) app_conversation.agent_status, RuntimeStatus.ERROR
)
else:
runtime_status = None
title = ( title = (
app_conversation.title app_conversation.title
or f'Conversation {base62.encodebytes(app_conversation.id.bytes)}' or f'Conversation {base62.encodebytes(app_conversation.id.bytes)}'
) )
return ConversationInfo( return ConversationInfo(
conversation_id=str(app_conversation.id), conversation_id=app_conversation.id.hex,
title=title, title=title,
last_updated_at=app_conversation.updated_at, last_updated_at=app_conversation.updated_at,
status=conversation_status, status=conversation_status,

View File

@@ -601,7 +601,7 @@ class TestDockerSandboxService:
service._docker_status_to_sandbox_status('paused') == SandboxStatus.PAUSED service._docker_status_to_sandbox_status('paused') == SandboxStatus.PAUSED
) )
assert ( assert (
service._docker_status_to_sandbox_status('exited') == SandboxStatus.MISSING service._docker_status_to_sandbox_status('exited') == SandboxStatus.PAUSED
) )
assert ( assert (
service._docker_status_to_sandbox_status('created') service._docker_status_to_sandbox_status('created')

View File

@@ -10,7 +10,7 @@ from openhands.resolver.issue_resolver import IssueResolver
def assert_sandbox_config( def assert_sandbox_config(
config: SandboxConfig, config: SandboxConfig,
base_container_image=SandboxConfig.model_fields['base_container_image'].default, base_container_image=SandboxConfig.model_fields['base_container_image'].default,
runtime_container_image='ghcr.io/all-hands-ai/runtime:mock-nikolaik', # Default to mock version runtime_container_image='ghcr.io/openhands/runtime:mock-nikolaik', # Default to mock version
local_runtime_url=SandboxConfig.model_fields['local_runtime_url'].default, local_runtime_url=SandboxConfig.model_fields['local_runtime_url'].default,
enable_auto_lint=False, enable_auto_lint=False,
): ):
@@ -38,7 +38,7 @@ def test_setup_sandbox_config_default():
assert_sandbox_config( assert_sandbox_config(
openhands_config.sandbox, openhands_config.sandbox,
runtime_container_image='ghcr.io/all-hands-ai/runtime:mock-nikolaik', runtime_container_image='ghcr.io/openhands/runtime:mock-nikolaik',
) )