mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Make CLI pip-installable (#8772)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
5fe7578f45
commit
4aed3944cf
2
.github/workflows/ghcr-build.yml
vendored
2
.github/workflows/ghcr-build.yml
vendored
@ -293,7 +293,7 @@ jobs:
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: make install-python-dependencies POETRY_GROUP=main,test,runtime INSTALL_PLAYWRIGHT=0
|
||||
run: make install-python-dependencies INSTALL_PLAYWRIGHT=0
|
||||
- name: Run docker runtime tests
|
||||
run: |
|
||||
# We install pytest-xdist in order to run tests across CPUs
|
||||
|
||||
2
.github/workflows/integration-runner.yml
vendored
2
.github/workflows/integration-runner.yml
vendored
@ -54,7 +54,7 @@ jobs:
|
||||
Hi! I started running the integration tests on your PR. You will receive a comment with the results shortly.
|
||||
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation
|
||||
run: poetry install --with dev,test,runtime
|
||||
|
||||
- name: Configure config.toml for testing with Haiku
|
||||
env:
|
||||
|
||||
4
.github/workflows/py-unit-tests.yml
vendored
4
.github/workflows/py-unit-tests.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'poetry'
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation
|
||||
run: poetry install --with dev,test,runtime
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Run Unit Tests
|
||||
@ -71,7 +71,7 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'poetry'
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation
|
||||
run: poetry install --with dev,test,runtime
|
||||
- name: Run Windows unit tests
|
||||
run: poetry run pytest -svv tests/unit/test_windows_bash.py
|
||||
- name: Run Windows runtime tests with LocalRuntime
|
||||
|
||||
2
Makefile
2
Makefile
@ -151,7 +151,7 @@ install-python-dependencies:
|
||||
echo "Installing only POETRY_GROUP=${POETRY_GROUP}"; \
|
||||
poetry install --only $${POETRY_GROUP}; \
|
||||
else \
|
||||
poetry install; \
|
||||
poetry install --with dev,test,runtime; \
|
||||
fi
|
||||
@if [ "${INSTALL_PLAYWRIGHT}" != "false" ] && [ "${INSTALL_PLAYWRIGHT}" != "0" ]; then \
|
||||
if [ -f "/etc/manjaro-release" ]; then \
|
||||
|
||||
@ -26,7 +26,7 @@ RUN apt-get update -y \
|
||||
|
||||
COPY ./pyproject.toml ./poetry.lock ./
|
||||
RUN touch README.md
|
||||
RUN export POETRY_CACHE_DIR && poetry install --without evaluation --no-root && rm -rf $POETRY_CACHE_DIR
|
||||
RUN export POETRY_CACHE_DIR && poetry install --no-root && rm -rf $POETRY_CACHE_DIR
|
||||
|
||||
FROM python:3.12.3-slim AS openhands-app
|
||||
|
||||
|
||||
@ -325,7 +325,7 @@ async def run_session(
|
||||
return new_session_requested
|
||||
|
||||
|
||||
async def main(loop: asyncio.AbstractEventLoop) -> None:
|
||||
async def main_with_loop(loop: asyncio.AbstractEventLoop) -> None:
|
||||
"""Runs the agent in CLI mode."""
|
||||
args = parse_arguments()
|
||||
|
||||
@ -417,11 +417,11 @@ async def main(loop: asyncio.AbstractEventLoop) -> None:
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def main():
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
try:
|
||||
loop.run_until_complete(main(loop))
|
||||
loop.run_until_complete(main_with_loop(loop))
|
||||
except KeyboardInterrupt:
|
||||
print('Received keyboard interrupt, shutting down...')
|
||||
except ConnectionRefusedError as e:
|
||||
@ -443,3 +443,7 @@ if __name__ == '__main__':
|
||||
except Exception as e:
|
||||
print(f'Error during cleanup: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
1170
poetry.lock
generated
1170
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -20,47 +20,36 @@ packages = [
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12,<3.14"
|
||||
litellm = "^1.60.0, !=1.64.4, !=1.67.*" # avoid 1.64.4 (known bug) & 1.67.* (known bug #10272)
|
||||
aiohttp = ">=3.9.0,!=3.11.13" # Pin to avoid yanked version 3.11.13
|
||||
google-generativeai = "*" # To use litellm with Gemini Pro API
|
||||
google-api-python-client = "^2.164.0" # For Google Sheets API
|
||||
google-auth-httplib2 = "*" # For Google Sheets authentication
|
||||
google-auth-oauthlib = "*" # For Google Sheets OAuth
|
||||
litellm = "^1.60.0, !=1.64.4, !=1.67.*" # avoid 1.64.4 (known bug) & 1.67.* (known bug #10272)
|
||||
aiohttp = ">=3.9.0,!=3.11.13" # Pin to avoid yanked version 3.11.13
|
||||
google-generativeai = "*" # To use litellm with Gemini Pro API
|
||||
google-api-python-client = "^2.164.0" # For Google Sheets API
|
||||
google-auth-httplib2 = "*" # For Google Sheets authentication
|
||||
google-auth-oauthlib = "*" # For Google Sheets OAuth
|
||||
termcolor = "*"
|
||||
docker = "*"
|
||||
fastapi = "*"
|
||||
toml = "*"
|
||||
uvicorn = "*"
|
||||
types-toml = "*"
|
||||
uvicorn = "*"
|
||||
numpy = "*"
|
||||
json-repair = "*"
|
||||
browsergym-core = "0.13.3" # integrate browsergym-core as the browsing interface
|
||||
browsergym-core = "0.13.3" # integrate browsergym-core as the browsing interface
|
||||
html2text = "*"
|
||||
e2b = ">=1.0.5,<1.4.0"
|
||||
pexpect = "*"
|
||||
jinja2 = "^3.1.3"
|
||||
python-multipart = "*"
|
||||
boto3 = "*"
|
||||
minio = "^7.2.8"
|
||||
tenacity = ">=8.5,<10.0"
|
||||
zope-interface = "7.2"
|
||||
pathspec = "^0.12.1"
|
||||
google-cloud-aiplatform = "*"
|
||||
anthropic = { extras = [ "vertex" ], version = "*" }
|
||||
tree-sitter = "^0.24.0"
|
||||
bashlex = "^0.18"
|
||||
pyjwt = "^2.9.0"
|
||||
dirhash = "*"
|
||||
python-frontmatter = "^1.1.0"
|
||||
python-docx = "*"
|
||||
PyPDF2 = "*"
|
||||
python-pptx = "*"
|
||||
pylatexenc = "*"
|
||||
tornado = "*"
|
||||
python-dotenv = "*"
|
||||
rapidfuzz = "^3.9.0"
|
||||
whatthepatch = "^1.0.6"
|
||||
protobuf = "^4.21.6,<5.0.0" # chromadb currently fails on 5.0+
|
||||
protobuf = "^4.21.6,<5.0.0" # chromadb currently fails on 5.0+
|
||||
opentelemetry-api = "1.25.0"
|
||||
opentelemetry-exporter-otlp-proto-grpc = "1.25.0"
|
||||
modal = ">=0.66.26,<0.78.0"
|
||||
@ -70,14 +59,8 @@ pygithub = "^2.5.0"
|
||||
joblib = "*"
|
||||
openhands-aci = "0.3.0"
|
||||
python-socketio = "^5.11.4"
|
||||
redis = ">=5.2,<7.0"
|
||||
sse-starlette = "^2.1.3"
|
||||
psutil = "*"
|
||||
stripe = ">=11.5,<13.0"
|
||||
ipywidgets = "^8.1.5"
|
||||
qtconsole = "^5.6.1"
|
||||
memory-profiler = "^0.61.0"
|
||||
daytona-sdk = "0.18.1"
|
||||
python-json-logger = "^3.2.1"
|
||||
prompt-toolkit = "^3.0.50"
|
||||
poetry = "^2.1.2"
|
||||
@ -85,6 +68,27 @@ anyio = "4.9.0"
|
||||
pythonnet = "*"
|
||||
fastmcp = "^2.5.2"
|
||||
mcpm = "1.12.0"
|
||||
python-frontmatter = "^1.1.0"
|
||||
# TODO: Should these go into the runtime group?
|
||||
ipywidgets = "^8.1.5"
|
||||
qtconsole = "^5.6.1"
|
||||
PyPDF2 = "*"
|
||||
python-pptx = "*"
|
||||
pylatexenc = "*"
|
||||
python-docx = "*"
|
||||
bashlex = "^0.18"
|
||||
|
||||
# TODO: These are integrations that should probably be optional
|
||||
redis = ">=5.2,<7.0"
|
||||
minio = "^7.2.8"
|
||||
daytona-sdk = "0.18.1"
|
||||
stripe = ">=11.5,<13.0"
|
||||
google-cloud-aiplatform = "*"
|
||||
anthropic = { extras = [ "vertex" ], version = "*" }
|
||||
boto3 = "*"
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "0.11.11"
|
||||
@ -93,6 +97,9 @@ pre-commit = "4.2.0"
|
||||
build = "*"
|
||||
types-setuptools = "*"
|
||||
|
||||
[tool.poetry.group.test]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
pytest = "*"
|
||||
pytest-cov = "*"
|
||||
@ -104,11 +111,18 @@ pandas = "*"
|
||||
reportlab = "*"
|
||||
gevent = ">=24.2.1,<26.0.0"
|
||||
|
||||
[tool.poetry.group.runtime]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.runtime.dependencies]
|
||||
jupyterlab = "*"
|
||||
notebook = "*"
|
||||
jupyter_kernel_gateway = "*"
|
||||
flake8 = "*"
|
||||
memory-profiler = "^0.61.0"
|
||||
|
||||
[tool.poetry.group.evaluation]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.evaluation.dependencies]
|
||||
streamlit = "*"
|
||||
@ -132,6 +146,7 @@ browsergym-visualwebarena = "0.13.3"
|
||||
boto3-stubs = { extras = [ "s3" ], version = "^1.37.19" }
|
||||
pyarrow = "20.0.0" # transitive dependency, pinned here to avoid conflicts
|
||||
datasets = "*"
|
||||
joblib = "*"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
openhands = "openhands.cli.main:main"
|
||||
|
||||
@ -1381,6 +1381,7 @@ async def test_first_user_message_with_identical_content(test_event_stream, mock
|
||||
await controller.close()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_agent_controller_processes_null_observation_with_cause():
|
||||
"""Test that AgentController processes NullObservation events with a cause value.
|
||||
|
||||
@ -1395,6 +1396,9 @@ async def test_agent_controller_processes_null_observation_with_cause():
|
||||
|
||||
# Create a mock agent with necessary attributes
|
||||
mock_agent = MagicMock(spec=Agent)
|
||||
mock_agent.get_system_message = MagicMock(
|
||||
return_value=None,
|
||||
)
|
||||
mock_agent.llm = MagicMock(spec=LLM)
|
||||
mock_agent.llm.metrics = Metrics()
|
||||
mock_agent.llm.config = OpenHandsConfig().get_llm_config()
|
||||
@ -1408,14 +1412,14 @@ async def test_agent_controller_processes_null_observation_with_cause():
|
||||
)
|
||||
|
||||
# Patch the controller's step method to track calls
|
||||
with patch.object(controller, 'step') as mock_step:
|
||||
with patch.object(controller, '_step') as mock_step:
|
||||
# Create and add the first user message (will have ID 0)
|
||||
user_message = MessageAction(content='First user message')
|
||||
user_message._source = EventSource.USER # type: ignore[attr-defined]
|
||||
event_stream.add_event(user_message, EventSource.USER)
|
||||
|
||||
# Give it a little time to process
|
||||
await asyncio.sleep(0.3)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Get all events from the stream
|
||||
events = list(event_stream.get_events())
|
||||
|
||||
@ -381,7 +381,7 @@ async def test_main_without_task(
|
||||
mock_run_session.return_value = False
|
||||
|
||||
# Run the function
|
||||
await cli.main(loop)
|
||||
await cli.main_with_loop(loop)
|
||||
|
||||
# Assertions
|
||||
mock_parse_args.assert_called_once()
|
||||
@ -458,7 +458,7 @@ async def test_main_with_task(
|
||||
mock_run_session.side_effect = [True, False]
|
||||
|
||||
# Run the function
|
||||
await cli.main(loop)
|
||||
await cli.main_with_loop(loop)
|
||||
|
||||
# Assertions
|
||||
mock_parse_args.assert_called_once()
|
||||
@ -553,7 +553,7 @@ async def test_main_with_session_name_passes_name_to_run_session(
|
||||
mock_run_session.return_value = False
|
||||
|
||||
# Run the function
|
||||
await cli.main(loop)
|
||||
await cli.main_with_loop(loop)
|
||||
|
||||
# Assertions
|
||||
mock_parse_args.assert_called_once()
|
||||
@ -713,7 +713,7 @@ async def test_main_security_check_fails(
|
||||
mock_check_security.return_value = False
|
||||
|
||||
# Run the function
|
||||
await cli.main(loop)
|
||||
await cli.main_with_loop(loop)
|
||||
|
||||
# Assertions
|
||||
mock_parse_args.assert_called_once()
|
||||
@ -796,7 +796,7 @@ async def test_config_loading_order(
|
||||
mock_run_session.return_value = False # No new session requested
|
||||
|
||||
# Run the function
|
||||
await cli.main(loop)
|
||||
await cli.main_with_loop(loop)
|
||||
|
||||
# Assertions for argument parsing and config setup
|
||||
mock_parse_args.assert_called_once()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user