mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54ead37412 |
@@ -2,6 +2,8 @@ This repository contains the code for OpenHands, an automated AI software engine
|
||||
(in the `openhands` directory) and React frontend (in the `frontend` directory).
|
||||
|
||||
## General Setup:
|
||||
To set up the entire repo, including frontend and backend, run `make build`.
|
||||
You don't need to do this unless the user asks you to, or if you're trying to run the entire application.
|
||||
|
||||
IMPORTANT: Before making any changes to the codebase, ALWAYS run `make install-pre-commit-hooks` to ensure pre-commit hooks are properly installed.
|
||||
|
||||
@@ -19,91 +21,13 @@ then re-run the command to ensure it passes. Common issues include:
|
||||
- Trailing whitespace
|
||||
- Missing newlines at end of files
|
||||
|
||||
## Testing and Debugging
|
||||
|
||||
### Environment Setup for Testing
|
||||
- Run `make build` to install all dependencies (only necessary for running tests):
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
**IMPORTANT**: When using `execute_bash` to run `make build` or similar long-running commands, set the `timeout` parameter to a high value (e.g., 600 seconds):
|
||||
```
|
||||
execute_bash(command="make build", timeout=600)
|
||||
```
|
||||
|
||||
#### Docker Installation
|
||||
**NOTE: Docker installation is ONLY required for running runtime tests with the Docker runtime.**
|
||||
|
||||
- Install Docker on Debian-based systems:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
|
||||
```
|
||||
- Start Docker daemon (in container environments without systemd):
|
||||
```bash
|
||||
sudo dockerd > /tmp/docker.log 2>&1 & sleep 5
|
||||
```
|
||||
- Verify Docker installation:
|
||||
```bash
|
||||
sudo docker run hello-world
|
||||
```
|
||||
|
||||
#### Development Environment Setup
|
||||
- Before running `make run`, ensure netcat is installed:
|
||||
```bash
|
||||
sudo apt-get install -y netcat-openbsd
|
||||
```
|
||||
|
||||
### Unit Tests
|
||||
- All unit tests are in `tests/unit/test_*.py`
|
||||
- To test new code, run `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate file for the current functionality
|
||||
- Write all tests with pytest
|
||||
|
||||
### Runtime Tests
|
||||
- Runtime tests are in `tests/runtime/test_*.py`
|
||||
- Run tests with different runtime implementations by setting the `TEST_RUNTIME` environment variable:
|
||||
```bash
|
||||
# Use Docker runtime (default)
|
||||
DEBUG=1 poetry run pytest -vvxss tests/runtime/test_bash.py
|
||||
|
||||
# Use CLI runtime (more reliable in some environments)
|
||||
DEBUG=1 TEST_RUNTIME=cli poetry run pytest -vvxss tests/runtime/test_bash.py
|
||||
|
||||
# Run a specific test
|
||||
DEBUG=1 TEST_RUNTIME=cli poetry run pytest -vvxss tests/runtime/test_bash.py::test_bash_server
|
||||
```
|
||||
- **IMPORTANT**: Runtime tests can take a long time to run, especially when building Docker images. Set a high timeout value:
|
||||
```
|
||||
execute_bash(command="DEBUG=1 poetry run pytest -vvxss tests/runtime/test_bash.py", timeout=600)
|
||||
```
|
||||
- The `DEBUG=1` flag enables more verbose logging
|
||||
- The `-vvxss` flags make the test output more verbose and stop after the first failure
|
||||
|
||||
### Debugging Docker Issues
|
||||
- Check Docker container status:
|
||||
```bash
|
||||
sudo docker ps -a
|
||||
```
|
||||
- View Docker logs:
|
||||
```bash
|
||||
sudo docker logs <container_id>
|
||||
```
|
||||
- Check Docker daemon logs:
|
||||
```bash
|
||||
sudo cat /tmp/docker.log | tail -n 100
|
||||
```
|
||||
- Check OpenHands logs:
|
||||
```bash
|
||||
cat logs/openhands_*.log | grep -i error | tail -n 20
|
||||
```
|
||||
|
||||
## Repository Structure
|
||||
Backend:
|
||||
- Located in the `openhands` directory
|
||||
- Testing:
|
||||
- All tests are in `tests/unit/test_*.py`
|
||||
- To test new code, run `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate file for the current functionality
|
||||
- Write all tests with pytest
|
||||
|
||||
Frontend:
|
||||
- Located in the `frontend` directory
|
||||
@@ -126,13 +50,6 @@ Frontend:
|
||||
|
||||
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
|
||||
|
||||
## Runtime Architecture
|
||||
- OpenHands uses a Docker-based runtime for secure execution of agent actions
|
||||
- The runtime builds a custom Docker image based on a specified base image
|
||||
- The image includes OpenHands-specific code and the runtime client
|
||||
- The runtime client executes actions in the sandboxed environment and returns observations
|
||||
- More details in the [runtime architecture documentation](https://docs.all-hands.dev/usage/architecture/runtime)
|
||||
|
||||
## Implementation Details
|
||||
|
||||
These details may or may not be useful for your current task.
|
||||
@@ -163,4 +80,4 @@ These details may or may not be useful for your current task.
|
||||
- Add the translation key to `frontend/src/i18n/declaration.ts`
|
||||
2. Add the setting to the backend:
|
||||
- Add the setting to the `Settings` model in `openhands/storage/data_models/settings.py`
|
||||
- Update any relevant backend code to apply the setting (e.g., in session creation)
|
||||
- Update any relevant backend code to apply the setting (e.g., in session creation)
|
||||
|
||||
+1
-1
@@ -136,7 +136,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
|
||||
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.41-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.42-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -51,17 +51,17 @@ system requirements and more information.
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.42
|
||||
```
|
||||
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||
|
||||
+3
-3
@@ -51,17 +51,17 @@ OpenHands也可以使用Docker在本地系统上运行。
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.42
|
||||
```
|
||||
|
||||
您将在[http://localhost:3000](http://localhost:3000)找到运行中的OpenHands!
|
||||
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.41-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.42-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ services:
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of openhands-state for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -46,7 +46,7 @@ poetry run python -m openhands.cli.main
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -55,7 +55,7 @@ docker run -it \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.42 \
|
||||
python -m openhands.cli.main --override-cli-mode true
|
||||
```
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ To run OpenHands in Headless mode with Docker:
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -42,7 +42,7 @@ docker run -it \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.42 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
|
||||
@@ -54,25 +54,25 @@ Check [the installation guide](/usage/local-setup) to make sure you have all the
|
||||
export LMSTUDIO_MODEL_NAME="imported-models/uncategorized/devstralq4_k_m.gguf" # <- Replace this with the model name you copied from LMStudio
|
||||
export LMSTUDIO_URL="http://host.docker.internal:1234" # <- Replace this with the port from LMStudio
|
||||
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik
|
||||
|
||||
mkdir -p ~/.openhands-state && echo '{"language":"en","agent":"CodeActAgent","max_iterations":null,"security_analyzer":null,"confirmation_mode":false,"llm_model":"lm_studio/'$LMSTUDIO_MODEL_NAME'","llm_api_key":"dummy","llm_base_url":"'$LMSTUDIO_URL/v1'","remote_runtime_resource_factor":null,"github_token":null,"enable_default_condenser":true,"user_consents_to_analytics":true}' > ~/.openhands-state/settings.json
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.42
|
||||
```
|
||||
|
||||
Once your server is running -- you can visit `http://localhost:3000` in your browser to use OpenHands with local Devstral model:
|
||||
```
|
||||
Digest: sha256:e72f9baecb458aedb9afc2cd5bc935118d1868719e55d50da73190d3a85c674f
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.42
|
||||
Starting OpenHands...
|
||||
Running OpenHands as root
|
||||
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
|
||||
|
||||
@@ -62,17 +62,17 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
|
||||
### Start the App
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.42-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.42
|
||||
```
|
||||
|
||||
You'll find OpenHands running at http://localhost:3000!
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.41.0",
|
||||
"version": "0.42.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.41.0",
|
||||
"version": "0.42.0",
|
||||
"dependencies": {
|
||||
"@heroui/react": "2.7.8",
|
||||
"@microlink/react-json-view": "^1.26.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.41.0",
|
||||
"version": "0.42.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
||||
@@ -36,6 +36,7 @@ class MCPProxyManager:
|
||||
Initialize the MCP Proxy Manager.
|
||||
|
||||
Args:
|
||||
name: Name of the proxy server
|
||||
auth_enabled: Whether authentication is enabled
|
||||
api_key: API key for authentication (required if auth_enabled is True)
|
||||
logger_level: Logging level for the FastMCP logger
|
||||
@@ -58,7 +59,7 @@ class MCPProxyManager:
|
||||
"""
|
||||
if len(self.config['mcpServers']) == 0:
|
||||
logger.info(
|
||||
'No MCP servers configured for FastMCP Proxy, skipping initialization.'
|
||||
f"No MCP servers configured for FastMCP Proxy, skipping initialization."
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -69,7 +70,7 @@ class MCPProxyManager:
|
||||
api_key=self.api_key,
|
||||
)
|
||||
|
||||
logger.info('FastMCP Proxy initialized successfully')
|
||||
logger.info(f"FastMCP Proxy initialized successfully")
|
||||
|
||||
async def mount_to_app(
|
||||
self, app: FastAPI, allow_origins: Optional[list[str]] = None
|
||||
@@ -82,7 +83,9 @@ class MCPProxyManager:
|
||||
allow_origins: List of allowed origins for CORS
|
||||
"""
|
||||
if len(self.config['mcpServers']) == 0:
|
||||
logger.info('No MCP servers configured for FastMCP Proxy, skipping mount.')
|
||||
logger.info(
|
||||
f"No MCP servers configured for FastMCP Proxy, skipping mount."
|
||||
)
|
||||
return
|
||||
|
||||
if not self.proxy:
|
||||
@@ -98,7 +101,8 @@ class MCPProxyManager:
|
||||
app.routes.remove('/mcp')
|
||||
|
||||
app.mount('/', mcp_app)
|
||||
logger.info('Mounted FastMCP Proxy app at /mcp')
|
||||
logger.info(f"Mounted FastMCP Proxy app at /mcp")
|
||||
|
||||
|
||||
async def update_and_remount(
|
||||
self,
|
||||
@@ -115,10 +119,13 @@ class MCPProxyManager:
|
||||
|
||||
Args:
|
||||
app: FastAPI application to mount to
|
||||
stdio_servers: List of stdio server configurations
|
||||
tools: List of tool configurations
|
||||
allow_origins: List of allowed origins for CORS
|
||||
"""
|
||||
tools = {t.name: t.model_dump() for t in stdio_servers}
|
||||
tools = {
|
||||
t.name: t.model_dump()
|
||||
for t in stdio_servers
|
||||
}
|
||||
self.config['mcpServers'] = tools
|
||||
|
||||
del self.proxy
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ requires = [
|
||||
|
||||
[tool.poetry]
|
||||
name = "openhands-ai"
|
||||
version = "0.41.0"
|
||||
version = "0.42.0"
|
||||
description = "OpenHands: Code Less, Make More"
|
||||
authors = [ "OpenHands" ]
|
||||
license = "MIT"
|
||||
|
||||
Reference in New Issue
Block a user