Remove poetry dependency in Jupyter Plugin (#9789)

This commit is contained in:
Boxuan Li 2025-07-18 11:54:53 -07:00 committed by GitHub
parent b96301061d
commit ee14f1ea41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 122 additions and 41 deletions

View File

@ -1,5 +1,5 @@
# Workflow that runs python unit tests
name: Run Python Unit Tests
# Workflow that runs python tests
name: Run Python Tests
# The jobs in this workflow are required, so they must run at all times
# * Always run on "main"
@ -16,9 +16,9 @@ concurrency:
cancel-in-progress: true
jobs:
# Run python unit tests on Linux
# Run python tests on Linux
test-on-linux:
name: Python Unit Tests on Linux
name: Python Tests on Linux
runs-on: blacksmith-4vcpu-ubuntu-2204
env:
INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation
@ -51,6 +51,8 @@ jobs:
run: poetry run pytest --forked -n auto -svv ./tests/unit
- name: Run Runtime Tests with CLIRuntime
run: TEST_RUNTIME=cli poetry run pytest -svv tests/runtime/test_bash.py
- name: Run E2E Tests
run: poetry run pytest -svv tests/e2e
# Run specific Windows python tests
test-on-windows:

View File

@ -8,8 +8,6 @@ from openhands.events.action import (
Action,
AgentFinishAction,
AgentRejectAction,
BrowseInteractiveAction,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
@ -17,7 +15,6 @@ from openhands.events.action import (
)
from openhands.events.observation import (
AgentStateChangedObservation,
BrowserOutputObservation,
CmdOutputMetadata,
CmdOutputObservation,
FileReadObservation,
@ -54,17 +51,19 @@ class DummyAgent(Agent):
},
{
'action': CmdRunAction(command='echo "foo"'),
'observations': [CmdOutputObservation('foo', command='echo "foo"')],
'observations': [
CmdOutputObservation(
'foo',
command='echo "foo"',
metadata=CmdOutputMetadata(exit_code=0),
)
],
},
{
'action': FileWriteAction(
content='echo "Hello, World!"', path='hello.sh'
),
'observations': [
FileWriteObservation(
content='echo "Hello, World!"', path='hello.sh'
)
],
'observations': [FileWriteObservation(content='', path='hello.sh')],
},
{
'action': FileReadAction(path='hello.sh'),
@ -76,36 +75,12 @@ class DummyAgent(Agent):
'action': CmdRunAction(command='bash hello.sh'),
'observations': [
CmdOutputObservation(
'bash: hello.sh: No such file or directory',
command='bash workspace/hello.sh',
metadata=CmdOutputMetadata(exit_code=127),
'Hello, World!',
command='bash hello.sh',
metadata=CmdOutputMetadata(exit_code=0),
)
],
},
{
'action': BrowseURLAction(url='https://google.com'),
'observations': [
BrowserOutputObservation(
'<html><body>Simulated Google page</body></html>',
url='https://google.com',
screenshot='',
trigger_by_action='',
),
],
},
{
'action': BrowseInteractiveAction(
browser_actions='goto("https://google.com")'
),
'observations': [
BrowserOutputObservation(
'<html><body>Simulated Google page after interaction</body></html>',
url='https://google.com',
screenshot='',
trigger_by_action='',
),
],
},
{
'action': AgentRejectAction(),
'observations': [AgentStateChangedObservation('', AgentState.REJECTED)],
@ -147,6 +122,47 @@ class DummyAgent(Agent):
obs.pop('timestamp', None)
obs.pop('cause', None)
obs.pop('source', None)
# Remove dynamic metadata fields that vary between runs
if 'extras' in obs and 'metadata' in obs['extras']:
metadata = obs['extras']['metadata']
if isinstance(metadata, dict):
metadata.pop('pid', None)
metadata.pop('username', None)
metadata.pop('hostname', None)
metadata.pop('working_dir', None)
metadata.pop('py_interpreter_path', None)
metadata.pop('suffix', None)
# Normalize file paths for comparison - extract just the filename
if 'extras' in obs and 'path' in obs['extras']:
path = obs['extras']['path']
if isinstance(path, str):
# Extract just the filename from the path
import os
obs['extras']['path'] = os.path.basename(path)
# Normalize message field to handle path differences
if 'message' in obs:
import os
message = obs['message']
if isinstance(message, str):
# Replace full paths with just filenames in messages
if 'I wrote to the file ' in message:
parts = message.split('I wrote to the file ')
if len(parts) == 2:
filename = os.path.basename(
parts[1].rstrip('.')
)
obs['message'] = (
f'I wrote to the file {filename}.'
)
elif 'I read the file ' in message:
parts = message.split('I read the file ')
if len(parts) == 2:
filename = os.path.basename(
parts[1].rstrip('.')
)
obs['message'] = f'I read the file {filename}.'
if hist_obs != expected_obs:
print(

View File

@ -635,7 +635,8 @@ def _create_server(
server_port=execution_server_port,
plugins=plugins,
app_config=config,
python_prefix=['poetry', 'run'],
python_prefix=[],
python_executable=sys.executable,
override_user_id=user_id,
override_username=username,
)

View File

@ -0,0 +1,62 @@
import os
import shutil
import subprocess
import tempfile
def test_headless_mode_with_dummy_agent_no_browser():
"""
E2E test: build a docker image from python:3.13, install openhands from source,
and run a local runtime task in headless mode.
"""
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))
dockerfile = """
FROM python:3.13-slim
WORKDIR /src
RUN apt-get update && apt-get install -y git build-essential tmux
COPY . /src
RUN pip install --upgrade pip setuptools wheel
RUN pip install .
ENV PYTHONUNBUFFERED=1
ENV RUNTIME=local
ENV RUN_AS_OPENHANDS=false
ENV ENABLE_BROWSER=false
ENV AGENT_ENABLE_BROWSING=false
ENV SKIP_DEPENDENCY_CHECK=1
CMD ["python", "-m", "openhands.core.main", "-c", "DummyAgent", "-t", "Hello world"]
"""
with tempfile.TemporaryDirectory() as tmpdir:
dockerfile_path = os.path.join(tmpdir, 'Dockerfile')
with open(dockerfile_path, 'w') as f:
f.write(dockerfile)
# Copy the repo into the temp dir for docker build context
build_context = os.path.join(tmpdir, 'context')
shutil.copytree(repo_root, build_context, dirs_exist_ok=True)
image_tag = 'openhands-e2e-local-runtime-test'
build_cmd = [
'docker',
'build',
'-t',
image_tag,
'-f',
dockerfile_path,
build_context,
]
run_cmd = ['docker', 'run', '--rm', image_tag]
# Build the image
build_proc = subprocess.run(build_cmd, capture_output=True, text=True)
print('Docker build stdout:', build_proc.stdout)
print('Docker build stderr:', build_proc.stderr)
assert build_proc.returncode == 0, 'Docker build failed'
# Run the container
run_proc = subprocess.run(run_cmd, capture_output=True, text=True)
print('Docker run stdout:', run_proc.stdout)
print('Docker run stderr:', run_proc.stderr)
assert run_proc.returncode == 0, (
f'Docker run failed with code {run_proc.returncode}'
)
assert 'Warning: Observation mismatch' not in run_proc.stdout