Compare commits

..

54 Commits

Author SHA1 Message Date
openhands
fc16da8fd2 Update microagent documentation to clarify that type field is optional 2025-06-08 19:39:17 +00:00
openhands
bd3ff43c67 Remove name field from microagent files 2025-06-08 19:35:06 +00:00
openhands
0fe5b808af Update microagent code to use filename as name when not specified 2025-06-08 19:34:59 +00:00
openhands
6c49686ff0 Add MCP tools documentation and update microagent field requirements 2025-06-08 19:30:21 +00:00
openhands
17212bb2f2 Remove unused fields from microagent code and update all microagent files 2025-06-08 19:26:56 +00:00
openhands
9d9f931e95 Remove unused fields from microagent documentation and example 2025-06-08 19:23:47 +00:00
openhands
6fe9680474 Consolidate task microagent documentation into keyword-triggered microagents 2025-06-08 19:19:44 +00:00
Xingyao Wang
53c80d1c92 Merge branch 'main' into update-microagent-docs 2025-06-08 15:17:37 -04:00
openhands
401262f353 Update documentation for task microagents with user input support 2025-06-08 19:15:31 +00:00
Xingyao Wang
58845b01a3 rename more files 2025-06-08 14:30:37 -04:00
Xingyao Wang
469d184157 address engel comment 2025-06-08 14:28:22 -04:00
Xingyao Wang
4837c4dc74 Update microagents/get_test_to_pass.md
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-06-09 02:24:23 +08:00
Xingyao Wang
6763f21cc3 Merge branch 'main' into add-back-microagents 2025-06-07 16:47:00 -04:00
Xingyao Wang
32e610ac1d revert unnecessary change 2025-06-07 16:30:55 -04:00
Xingyao Wang
85c65391ca revert changes 2025-06-03 13:53:27 -04:00
Xingyao Wang
c444dbfbbf remove fe changes 2025-06-03 12:04:37 -04:00
Xingyao Wang
dd988d0f14 revert fe 2025-06-03 12:03:00 -04:00
Xingyao Wang
6f1a74e286 merge main 2025-06-03 11:37:51 -04:00
Xingyao Wang
7b956b6103 revert docs to look like main 2025-06-03 11:35:57 -04:00
openhands
34b097115d Fix linting issues in frontend and Python code 2025-05-19 01:39:48 +00:00
openhands
3e4ab4f379 Fix docstring formatting in KnowledgeMicroagent class 2025-05-19 01:29:23 +00:00
openhands
54cd9f7e44 Fix unlocalized strings in microagent-dropdown.tsx 2025-05-19 01:26:33 +00:00
openhands
802b765f98 Add microagent button and dropdown to trajectory actions 2025-05-17 12:05:13 +00:00
openhands
18c88f99ff Merge from main to resolve conflicts 2025-05-17 06:56:11 +00:00
openhands
f3934be07b Fix microagent suggestions using tippy.js for better popup handling 2025-05-12 12:55:00 +00:00
openhands
6ce9f49d1e Fix linting issues in TipTap editor component 2025-05-12 11:06:15 +00:00
openhands
fc07622b20 Implement microagent suggestions using TipTap 2025-05-12 11:00:08 +00:00
Xingyao Wang
da935f9d8f Merge branch 'main' into add-back-microagents 2025-05-03 00:04:17 +08:00
openhands
642cc52a1a Fix linting issues in handlers.ts 2025-05-02 13:06:21 +00:00
openhands
4c361ab9e5 Add mock handler for microagents endpoint 2025-05-02 09:23:25 +00:00
openhands
5dfa1bb6eb Fix microagent suggestions UI and TypeScript errors 2025-05-02 09:21:15 +00:00
Xingyao Wang
a07cf972a5 Merge commit '6032d2620d6ec252d3c80695a6de1fc88da9c87a' into add-back-microagents 2025-05-02 09:03:17 +00:00
openhands
f2e3bc3254 Fix microagent suggestions feature 2025-05-02 08:52:19 +00:00
openhands
3790ec7d60 Add tests for microagent suggestions component 2025-05-02 03:31:41 +00:00
openhands
3c0719309e Add microagent suggestions feature to chat input 2025-05-02 02:57:57 +00:00
Xingyao Wang
0236e0943e fix test 2025-05-02 02:09:27 +00:00
Xingyao Wang
cd464c0022 rename files 2025-05-01 10:38:04 +08:00
Xingyao Wang
4519a7f4f3 fix test 2025-05-01 02:29:52 +00:00
Xingyao Wang
fdc591330b add remain 2025-05-01 02:25:38 +00:00
Xingyao Wang
98e454e82c fix lint and missing imports 2025-05-01 02:25:24 +00:00
Xingyao Wang
e088d2d24a simplify microagent 2025-05-01 02:13:46 +00:00
Xingyao Wang
58c574af1e revert changes 2025-05-01 02:13:00 +00:00
Xingyao Wang
405f0069f8 revert some changes 2025-05-01 02:03:06 +00:00
Xingyao Wang
f26d770d03 remove hardcoded last line 2025-05-01 02:01:51 +00:00
Xingyao Wang
bf2c3de219 cleanup tests 2025-04-30 11:11:23 +08:00
Xingyao Wang
7c35ce16e5 Merge branch 'main' into add-back-microagents 2025-04-30 11:07:17 +08:00
Xingyao Wang
f4024ccd94 Update microagents/update_pr_description.md
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-04-30 10:43:37 +08:00
Xingyao Wang
b55bfed831 Update microagents/address_pr_comments.md
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-04-30 10:38:22 +08:00
OpenHands Bot
cb0994027f 🤖 Auto-fix Python linting issues 2025-04-29 16:02:04 +00:00
openhands
bcc9bd0b9a Move task microagent tests to test_microagent_task.py 2025-04-29 02:12:14 +00:00
openhands
6c144e6b5a Add back microagent files with special handling for user inputs 2025-04-29 02:06:42 +00:00
openhands
e90b841b0d Update microagent files to match original ones with added triggers and variable prompts 2025-04-29 01:48:10 +00:00
openhands
a1e6ed4dff Add special handling for microagents that require user input 2025-04-29 01:47:18 +00:00
openhands
ad6311d3cd Add back microagent files and add special handling for user input variables 2025-04-29 01:33:23 +00:00
2835 changed files with 101578 additions and 334034 deletions

View File

@@ -1,37 +0,0 @@
---
name: upcoming-release
description: This skill should be used when the user asks to "generate release notes", "list upcoming release PRs", "summarize upcoming release", "/upcoming-release", or needs to know what changes are part of an upcoming release.
---
# Upcoming Release Summary
Generate a concise summary of PRs included in the upcoming release.
## Prerequisites
Two commit SHAs are required:
- **First SHA**: The older commit (current release)
- **Second SHA**: The newer commit (what's being released)
If the user does not provide both SHAs, ask for them before proceeding.
## Workflow
1. Run the script from the repository root with the `--json` flag:
```bash
.github/scripts/find_prs_between_commits.py <older-sha> <newer-sha> --json
```
2. Filter out PRs that are:
- Chores
- Dependency updates
- Adding logs
- Refactors
3. Categorize the remaining PRs:
- **Features** - New functionality
- **Bug fixes** - Corrections to existing behavior
- **Security/CVE fixes** - Security-related changes
- **Other** - Everything else
4. Format the output with PRs listed under their category, including the PR number and a brief description.

View File

@@ -1,123 +0,0 @@
---
name: update-sdk
description: This skill should be used when the user asks to "update SDK", "bump SDK version", "pin SDK to a commit", "test unreleased SDK", "update agent-server image", "bump the version", "prepare a release", "what files change for a release", or needs to know how SDK packages are managed in the OpenHands repository. For detailed reference material, see references/docker-image-locations.md and references/sdk-pinning-examples.md in this skill directory.
---
# Update SDK
Bump SDK packages (`openhands-sdk`, `openhands-agent-server`, `openhands-tools`), pin them to unreleased commits for testing, and cut an OpenHands release.
## Quick Summary — How Many Files Change?
| Activity | Manual edits | Auto-regenerated | Total |
|----------|:------------:|:----------------:|:-----:|
| **SDK bump** (released PyPI version) | 2 | 3 | **5** |
| **SDK pin** (unreleased git commit) | 3 | 3 | **6** |
| **Release commit** (version bump) | 3 | 0 | **3** |
The 3 auto-regenerated files are always: `poetry.lock`, `uv.lock`, `enterprise/poetry.lock`.
## SDK Package Bump — 2 Files + 3 Lock Files
Land as a separate PR before the release. Examples: `929dcc3` (SDK 1.11.5), `cd235cc` (SDK 1.11.4).
| File | What to change |
|------|----------------|
| `pyproject.toml` | `openhands-sdk`, `openhands-agent-server`, `openhands-tools` in **two** sections: the `dependencies` array (PEP 508) **and** `[tool.poetry.dependencies]` |
| `openhands/app_server/sandbox/sandbox_spec_service.py` | `AGENT_SERVER_IMAGE` constant — set to `ghcr.io/openhands/agent-server:<version>-python` |
Then regenerate lock files:
```bash
poetry lock && uv lock && cd enterprise && poetry lock && cd ..
```
## Docker Image Locations — All Hardcoded References
For the complete inventory of every file containing a hardcoded Docker image tag or repository, see `references/docker-image-locations.md`. Key files that must stay in sync during an SDK bump:
| File | Image reference | Updated during SDK bump? |
|------|----------------|:------------------------:|
| `openhands/app_server/sandbox/sandbox_spec_service.py` | `AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:<tag>-python'` | ✅ Yes |
| `docker-compose.yml` | `AGENT_SERVER_IMAGE_TAG` default | ✅ Should be |
| `containers/dev/compose.yml` | `AGENT_SERVER_IMAGE_REPOSITORY` + `_TAG` defaults | ✅ Should be |
> **CI enforcement:** `.github/workflows/check-version-consistency.yml` validates version consistency and compose file image references on every PR and push to main.
### ⚠️ Docker Image Tag Gotcha (merge-commit SHA)
The SDK CI in `software-agent-sdk` repo tags Docker images with the **GitHub Actions merge-commit SHA**, NOT the PR head-commit SHA. When pinning to an SDK PR branch:
1. Check the SDK PR description for the actual image tag (look for the `AGENT_SERVER_IMAGES` section)
2. Or query the CI logs: the "Consolidate Build Information" job prints `"short_sha": "<tag>"`
3. The merge-commit SHA differs from the head SHA shown in the PR
For released SDK versions, images use a version tag (e.g., `1.12.0-python`) — no merge-commit ambiguity.
## Cutting a Release — 3 Files
A release commit updates the version string across 3 files. Gold-standard examples: 1.3.0 (`d063c8c`), 1.4.0 (`495f48b`).
| File | What to change |
|------|----------------|
| `pyproject.toml` | `version = "X.Y.Z"` under `[tool.poetry]` |
| `frontend/package.json` | `"version": "X.Y.Z"` |
| `frontend/package-lock.json` | `"version": "X.Y.Z"` in **two** places (root object and `packages[""]`) |
> **Note:** `openhands/version.py` reads the version from `pyproject.toml` at runtime — no manual edit needed there.
### Compose Files (2 files)
Both compose files should use `ghcr.io/openhands/agent-server` with the current SDK version tag.
| File | What to verify |
|------|----------------|
| `docker-compose.yml` | `AGENT_SERVER_IMAGE_REPOSITORY` defaults to agent-server, `AGENT_SERVER_IMAGE_TAG` is current |
| `containers/dev/compose.yml` | Same — must use agent-server, not runtime |
### Release Workflow
#### Step 1: Verify the SDK bump has landed
```bash
grep -n "openhands-sdk\|openhands-agent-server\|openhands-tools" pyproject.toml
grep -n "AGENT_SERVER_IMAGE" openhands/app_server/sandbox/sandbox_spec_service.py
grep "AGENT_SERVER_IMAGE_TAG" docker-compose.yml containers/dev/compose.yml
```
#### Step 2: Bump version numbers
```bash
# Edit pyproject.toml, frontend/package.json, frontend/package-lock.json
git add pyproject.toml frontend/package.json frontend/package-lock.json
git commit -m "Release X.Y.Z"
git tag X.Y.Z
```
Create a `saas-rel-X.Y.Z` branch from the tagged commit for the SaaS deployment pipeline.
#### Step 3: CI builds Docker images automatically
The `ghcr-build.yml` workflow triggers on tag pushes and produces:
- `ghcr.io/openhands/openhands:X.Y.Z`, `X.Y`, `X`, `latest`
- `ghcr.io/openhands/runtime:X.Y.Z-nikolaik`, `X.Y-nikolaik`
The tagging logic lives in `containers/build.sh` — when `GITHUB_REF_NAME` matches a semver pattern (`^[0-9]+\.[0-9]+\.[0-9]+$`), it auto-generates major, major.minor, and `latest` tags.
## Development: Pin SDK to an Unreleased Commit
For detailed examples of all pinning formats (commit, branch, uv-only), see `references/sdk-pinning-examples.md`.
### Files to change (3 manual + 3 lock files)
| File | What to change |
|------|----------------|
| `pyproject.toml` | Pin all 3 SDK packages in **both** `dependencies` and `[tool.poetry.dependencies]` |
| `openhands/app_server/sandbox/sandbox_spec_service.py` | `AGENT_SERVER_IMAGE` — use the merge-commit SHA tag, NOT the head-commit SHA |
| `docker-compose.yml` | `AGENT_SERVER_IMAGE_TAG` default (for local development) |
| `poetry.lock` | Auto-regenerated via `poetry lock` |
| `uv.lock` | Auto-regenerated via `uv lock` |
| `enterprise/poetry.lock` | Auto-regenerated via `cd enterprise && poetry lock` |
### CI guard
The `check-package-versions.yml` workflow blocks merging to `main` if `[tool.poetry.dependencies]` contains any `rev` fields. This ensures unreleased SDK pins do not accidentally ship in a release.

View File

@@ -1,84 +0,0 @@
# Docker Image Locations — Complete Inventory
Every file in the OpenHands repository containing a hardcoded Docker image tag, repository, or version-pinned image reference. Organized by update cadence.
## Updated During SDK Bump (must change)
These files contain image tags that **must** be updated whenever the SDK version or pinned commit changes.
### `openhands/app_server/sandbox/sandbox_spec_service.py`
- **Line:** `AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:<tag>-python'`
- **Format:** `<sdk-version>-python` for releases (e.g., `1.12.0-python`), `<7-char-commit-hash>-python` for dev pins
- **Source of truth** for which agent-server image the app server pulls at runtime
- **⚠️ Gotcha:** When pinning to an SDK PR, the image tag is the **merge-commit SHA** from GitHub Actions, not the PR head-commit SHA. Check the SDK PR description or CI logs for the correct tag.
### `docker-compose.yml`
- **Lines:**
```yaml
- AGENT_SERVER_IMAGE_REPOSITORY=${AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server}
- AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-<tag>-python}
```
- Used by `docker compose up` for local development
### `containers/dev/compose.yml`
- **Lines:**
```yaml
- AGENT_SERVER_IMAGE_REPOSITORY=${AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server}
- AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-<tag>-python}
```
- Used by the dev container setup
- **Known issue:** On main as of 1.4.0, this file still points to `ghcr.io/openhands/runtime` instead of `agent-server`, and the tag is `1.2-nikolaik` (stale from the V0 era). The `check-version-consistency.yml` CI workflow catches this.
## Updated During Release Commit (version string only)
### `pyproject.toml`
- **Line:** `version = "X.Y.Z"` under `[tool.poetry]`
- The Python version is derived from this at runtime via `openhands/version.py`
### `frontend/package.json`
- **Line:** `"version": "X.Y.Z"`
### `frontend/package-lock.json`
- **Two places:** root `"version": "X.Y.Z"` and `packages[""].version`
## Dynamic References (auto-derived, no manual update)
### `openhands/version.py`
- Reads version from `pyproject.toml` at runtime → `openhands.__version__`
### `openhands/resolver/issue_resolver.py`
- Builds `ghcr.io/openhands/runtime:{openhands.__version__}-nikolaik` dynamically
### `openhands/runtime/utils/runtime_build.py`
- Base repo URL `ghcr.io/openhands/runtime` is a constant; version comes from elsewhere
### `.github/scripts/update_pr_description.sh`
- Uses `${SHORT_SHA}` variable at CI runtime, not hardcoded
### `enterprise/Dockerfile`
- `ARG BASE="ghcr.io/openhands/openhands"` — base image, version supplied at build time
## V0 Legacy Files (separate update cadence)
These reference the V0 runtime image (`ghcr.io/openhands/runtime:X.Y-nikolaik`) for local Docker/Kubernetes paths. They are **not** updated as part of a V1 release but may be updated independently.
### `Development.md`
- `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:X.Y-nikolaik`
### `openhands/runtime/impl/kubernetes/README.md`
- `runtime_container_image = "docker.openhands.dev/openhands/runtime:X.Y-nikolaik"`
### `enterprise/enterprise_local/README.md`
- Uses `ghcr.io/openhands/runtime:main-nikolaik` (points to `main`, not versioned)
### `third_party/runtime/impl/daytona/README.md`
- Uses `${OPENHANDS_VERSION}` variable, not hardcoded
## Image Registries
| Registry | Usage |
|----------|-------|
| `ghcr.io/openhands/agent-server` | V1 agent-server (sandbox) — built by SDK repo CI |
| `ghcr.io/openhands/openhands` | Main app image — built by `ghcr-build.yml` |
| `ghcr.io/openhands/runtime` | V0 runtime sandbox — built by `ghcr-build.yml` |
| `docker.openhands.dev/openhands/*` | Mirror/CDN for the above images |

View File

@@ -1,103 +0,0 @@
# SDK Pinning Examples
Examples from real commits showing how to pin SDK packages to unreleased commits, branches, or released versions.
## Pin to a Specific Commit
Example from commit `169fb76` (pinning all 3 packages to SDK commit `100e9af`):
### `dependencies` array (PEP 508 format)
```toml
"openhands-agent-server @ git+https://github.com/OpenHands/software-agent-sdk.git@100e9af#subdirectory=openhands-agent-server",
"openhands-sdk @ git+https://github.com/OpenHands/software-agent-sdk.git@100e9af#subdirectory=openhands-sdk",
"openhands-tools @ git+https://github.com/OpenHands/software-agent-sdk.git@100e9af#subdirectory=openhands-tools",
```
### `[tool.poetry.dependencies]` (Poetry format)
```toml
openhands-sdk = { git = "https://github.com/OpenHands/software-agent-sdk.git", rev = "100e9af", subdirectory = "openhands-sdk" }
openhands-agent-server = { git = "https://github.com/OpenHands/software-agent-sdk.git", rev = "100e9af", subdirectory = "openhands-agent-server" }
openhands-tools = { git = "https://github.com/OpenHands/software-agent-sdk.git", rev = "100e9af", subdirectory = "openhands-tools" }
```
### `openhands/app_server/sandbox/sandbox_spec_service.py`
```python
AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:<merge-commit-sha>-python'
```
**⚠️ Important:** The image tag is the **merge-commit SHA** from the SDK CI, not the commit hash used in `pyproject.toml`. Look up the correct tag from the SDK PR description or CI logs.
## Pin to a Branch
Example from commit `430ee1c` (pinning to branch `openhands/issue-2228-sdk-settings-schema`):
### `[tool.poetry.dependencies]`
```toml
openhands-sdk = { git = "https://github.com/OpenHands/software-agent-sdk.git", branch = "openhands/issue-2228-sdk-settings-schema", subdirectory = "openhands-sdk" }
openhands-agent-server = { git = "https://github.com/OpenHands/software-agent-sdk.git", branch = "openhands/issue-2228-sdk-settings-schema", subdirectory = "openhands-agent-server" }
openhands-tools = { git = "https://github.com/OpenHands/software-agent-sdk.git", branch = "openhands/issue-2228-sdk-settings-schema", subdirectory = "openhands-tools" }
```
## Using `[tool.uv.sources]` Override
When only `uv` needs the override (keep PyPI versions in the main arrays), add a `[tool.uv.sources]` section. Example from commit `1daca49`:
```toml
[tool.uv.sources]
openhands-sdk = { git = "https://github.com/OpenHands/software-agent-sdk.git", subdirectory = "openhands-sdk", rev = "4170cca" }
openhands-agent-server = { git = "https://github.com/OpenHands/software-agent-sdk.git", subdirectory = "openhands-agent-server", rev = "4170cca" }
openhands-tools = { git = "https://github.com/OpenHands/software-agent-sdk.git", subdirectory = "openhands-tools", rev = "4170cca" }
```
## Released PyPI Version (standard release)
Example from commit `929dcc3` (SDK 1.11.5):
### `dependencies` array
```toml
"openhands-agent-server==1.11.5",
"openhands-sdk==1.11.5",
"openhands-tools==1.11.5",
```
### `[tool.poetry.dependencies]`
```toml
openhands-sdk = "1.11.5"
openhands-agent-server = "1.11.5"
openhands-tools = "1.11.5"
```
### `openhands/app_server/sandbox/sandbox_spec_service.py`
For released versions, the image tag uses the version number:
```python
AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:1.11.5-python'
```
However, **some releases use a commit-hash tag** even for the released version. Check which tag format exists on GHCR. Example from `929dcc3`:
```python
AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:010e847-python'
```
## Regenerate Lock Files
After any change to `pyproject.toml`, always regenerate:
```bash
poetry lock
uv lock
cd enterprise && poetry lock && cd ..
```
## CI Guards
- **`check-package-versions.yml`**: Blocks merge to `main` if `[tool.poetry.dependencies]` contains `rev` fields (prevents shipping unreleased SDK pins)
- **`check-version-consistency.yml`**: Validates version strings match across `pyproject.toml`, `package.json`, `package-lock.json`, and verifies compose files use `agent-server` images

View File

@@ -1 +0,0 @@
This way of running OpenHands is not officially supported. It is maintained by the community.

View File

@@ -12,8 +12,5 @@
"ghcr.io/devcontainers/features/node:1": {},
},
"postCreateCommand": ".devcontainer/setup.sh",
"runArgs": ["--add-host=host.docker.internal:host-gateway"],
"containerEnv": {
"DOCKER_HOST_ADDR": "host.docker.internal"
},
"runArgs": ["--network=host"],
}

View File

@@ -1,14 +1,7 @@
#!/bin/bash
# Mark the current repository as safe for Git to prevent "dubious ownership" errors,
# which can occur in containerized environments when directory ownership doesn't match the current user.
git config --global --add safe.directory "$(realpath .)"
# Install `nc`
sudo apt update && sudo apt install netcat -y
# Install `uv` and `uvx`
wget -qO- https://astral.sh/uv/install.sh | sh
# Do common setup tasks
source .openhands/setup.sh

View File

@@ -1,23 +1,5 @@
# NodeJS
frontend/node_modules
# Configuration (except pyproject.toml)
*.ini
*.toml
!pyproject.toml
*.yml
# Documentation (except README.md)
*.md
!README.md
# Hidden files and directories
.*
__pycache__
# Unneded files and directories
/dev_config/
/docs/
/evaluation/
/tests/
CITATION.cff
config.toml
.envrc
.env
.git

11
.github/CODEOWNERS vendored
View File

@@ -1,8 +1,11 @@
# CODEOWNERS file for OpenHands repository
# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
/frontend/ @amanape @hieptl
/openhands-ui/ @amanape @hieptl
/openhands/ @tofarr @malhotra5 @hieptl
/enterprise/ @chuckbutkus @tofarr @malhotra5
# Frontend code owners
/frontend/ @rbren @amanape
# Evaluation code owners
/evaluation/ @xingyaoww @neubig
# Documentation code owners
/docs/ @mamoodi

View File

@@ -5,113 +5,44 @@ labels: ['bug']
body:
- type: markdown
attributes:
value: |
## Thank you for reporting a bug! 🐛
**Please fill out all required fields.** Issues missing critical information (version, installation method, reproduction steps, etc.) will be delayed or closed until complete details are provided.
Clear, detailed reports help us resolve issues faster.
value: Thank you for taking the time to fill out this bug report. Please provide as much information as possible
to help us understand and address the issue effectively.
- type: checkboxes
attributes:
label: Is there an existing issue for the same bug?
description: Please search existing issues before creating a new one. If found, react or comment to the duplicate issue instead of making a new one.
label: Is there an existing issue for the same bug? (If one exists, thumbs up or comment on the issue instead).
description: Please check if an issue already exists for the bug you encountered.
options:
- label: I have searched existing issues and this is not a duplicate.
- label: I have checked the existing issues.
required: true
- type: textarea
id: bug-description
attributes:
label: Bug Description
description: Clearly describe what went wrong. Be specific and concise.
placeholder: Example - "When I run a Python task, OpenHands crashes after 30 seconds with a connection timeout error."
label: Describe the bug and reproduction steps
description: Provide a description of the issue along with any reproduction steps.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: What did you expect to happen?
placeholder: Example - "OpenHands should execute the Python script and return results."
validations:
required: false
- type: textarea
id: actual-behavior
attributes:
label: Actual Behavior
description: What actually happened?
placeholder: Example - "Connection timed out after 30 seconds, task failed with error code 500."
validations:
required: false
- type: textarea
id: reproduction-steps
attributes:
label: Steps to Reproduce
description: Provide clear, step-by-step instructions to reproduce the bug.
placeholder: |
1. Install OpenHands using Docker
2. Configure with Claude 3.5 Sonnet
3. Run command: `openhands run "write a python script"`
4. Wait 30 seconds
5. Error appears
validations:
required: false
- type: dropdown
id: installation
attributes:
label: OpenHands Installation Method
label: OpenHands Installation
description: How are you running OpenHands?
options:
- CLI (uv tool install)
- CLI (executable binary)
- CLI (Docker)
- Local GUI (Docker web interface)
- OpenHands Cloud (app.all-hands.dev)
- SDK (Python library)
- Docker command in README
- GitHub resolver
- Development workflow
- app.all-hands.dev
- Other
default: 0
validations:
required: false
- type: input
id: installation-other
attributes:
label: If you selected "Other", please specify
description: Describe your installation method
placeholder: ex. Custom Kubernetes deployment, pip install from source, etc.
- type: input
id: openhands-version
attributes:
label: OpenHands Version
description: What version are you using? Find this in settings or by running `openhands --version`
placeholder: ex. 0.9.8, main, commit hash, etc.
validations:
required: false
- type: checkboxes
id: version-confirmation
attributes:
label: Version Confirmation
description: Bugs on older versions may already be fixed. Please upgrade before submitting.
options:
- label: "I have confirmed this bug exists on the LATEST version of OpenHands"
required: false
- type: input
id: model-name
attributes:
label: Model Name
description: Which LLM model are you using?
placeholder: ex. gpt-4o, claude-3-5-sonnet-20241022, openrouter/deepseek-r1, etc.
validations:
required: false
description: What version of OpenHands are you using?
placeholder: ex. 0.9.8, main, etc.
- type: dropdown
id: os
@@ -121,46 +52,12 @@ body:
- MacOS
- Linux
- WSL on Windows
- Windows (Docker Desktop)
- Other
validations:
required: false
- type: input
id: browser
attributes:
label: Browser (if using web UI)
description: |
If applicable, which browser and version?
placeholder: ex. Chrome 131, Firefox 133, Safari 17.2
- type: textarea
id: logs
attributes:
label: Logs and Error Messages
description: |
**Paste relevant logs, error messages, or stack traces.** Use code blocks (```) for formatting.
LLM logs are in `logs/llm/default/`. Include timestamps if errors occurred at a specific time.
placeholder: |
```
Paste error logs here
```
- type: textarea
id: additional-context
attributes:
label: Screenshots and Additional Context
description: |
Add screenshots, videos, runtime environment, or other context that helps explain the issue.
💡 **Share conversation history:** In the OpenHands chat UI, click the 👎 or 👍 button (above the message input) to generate a shareable link to your conversation.
placeholder: Drag and drop screenshots here, paste links, or add additional context.
- type: markdown
attributes:
value: |
---
**Note:** Issues with incomplete information may be closed or deprioritized. Maintainers and community members have limited bandwidth and prioritize well-documented bugs that are easier to reproduce and fix. Thank you for your understanding!
label: Logs, Errors, Screenshots, and Additional Context
description: Please provide any additional information you think might help. If you want to share the chat history
you can click the thumbs-down (👎) button above the input field and you will get a shareable link
(you can also click thumbs up when things are going well of course!). LLM logs will be stored in the
`logs/llm/default` folder. Please add any additional context about the problem here.

View File

@@ -1,2 +0,0 @@
# disable blank issue creation
blank_issues_enabled: false

View File

@@ -0,0 +1,17 @@
---
name: Feature Request or Enhancement
about: Suggest an idea for an OpenHands feature or enhancement
title: ''
labels: 'enhancement'
assignees: ''
---
**What problem or use case are you trying to solve?**
**Describe the UX or technical implementation you have in mind**
**Additional context**
### If you find this feature request or enhancement useful, make sure to add a 👍 to the issue

View File

@@ -1,105 +0,0 @@
name: Feature Request or Enhancement
description: Suggest a new feature or improvement for OpenHands
title: '[Feature]: '
labels: ['enhancement']
body:
- type: markdown
attributes:
value: |
## Thank you for suggesting a feature! 💡
**Please provide detailed information.** Vague or low-effort requests may be closed. Well-documented feature requests with strong community support are more likely to be added to the roadmap.
- type: checkboxes
attributes:
label: Is there an existing feature request for this?
description: Please search existing issues and feature requests before creating a new one. If found, react or comment to the duplicate issue instead of making a new one.
options:
- label: I have searched existing issues and feature requests, and this is not a duplicate.
required: true
- type: textarea
id: problem-statement
attributes:
label: Problem or Use Case
description: What problem are you trying to solve? What use case would this feature enable?
placeholder: |
Example - "As a developer working on large codebases, I need to search across multiple files simultaneously. Currently, I have to search file-by-file which is time-consuming and inefficient."
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed Solution
description: Describe your ideal solution. What should this feature do? How should it work?
placeholder: |
Example - "Add a global search feature that allows searching across all files in the workspace. Results should show file name, line number, and context around matches. Include regex support and filtering options."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Have you considered any alternative solutions or workarounds? What are their limitations?
placeholder: Example - "I tried using grep in the terminal, but it's not integrated with the UI and doesn't provide click-to-navigate functionality."
- type: dropdown
id: priority
attributes:
label: Priority / Severity
description: How important is this feature to your workflow?
options:
- "Critical - Blocking my work, no workaround available"
- "High - Significant impact on productivity"
- "Medium - Would improve experience"
- "Low - Nice to have"
default: 2
validations:
required: true
- type: dropdown
id: scope
attributes:
label: Estimated Scope
description: To the best of your knowledge, how complex do you think this feature would be to implement?
options:
- "Small - UI tweak, config option, or minor change"
- "Medium - New feature with moderate complexity"
- "Large - Significant feature requiring architecture changes"
- "Unknown - Not sure about the technical complexity"
default: 3
- type: dropdown
id: feature-area
attributes:
label: Feature Area
description: Which part of OpenHands does this feature relate to? If you select "Other", please specify the area in the Additional Context section below.
options:
- "Agent / AI behavior"
- "User Interface / UX"
- "CLI / Command-line interface"
- "File system / Workspace management"
- "Configuration / Settings"
- "Integrations (GitHub, GitLab, etc.)"
- "Performance / Optimization"
- "Documentation"
- "Other"
validations:
required: true
- type: textarea
id: technical-details
attributes:
label: Technical Implementation Ideas (Optional)
description: If you have technical expertise, share implementation ideas, API suggestions, or relevant technical details.
placeholder: |
Example - "Could use ripgrep library for fast search. Expose results via /api/search endpoint. Frontend can use virtualized list for rendering large result sets."
- type: textarea
id: additional-context
attributes:
label: Additional Context
description: Add any other context, screenshots, mockups, or examples that help illustrate this feature request.
placeholder: Drag and drop screenshots, mockups, or links here.

View File

@@ -16,6 +16,7 @@ updates:
mcp-packages:
patterns:
- "mcp"
- "mcpm"
security-all:
applies-to: "security-updates"
patterns:
@@ -72,9 +73,3 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "docker"
directories:
- "containers/*"
schedule:
interval: "weekly"

View File

@@ -1,38 +1,12 @@
<!-- If you are still working on the PR, please mark it as draft. Maintainers will review PRs marked ready for review, which leads to lost time if your PR is actually not ready yet. Keep the PR marked as draft until it is finally ready for review -->
- [ ] This change is worth documenting at https://docs.all-hands.dev/
- [ ] Include this change in the Release Notes. If checked, you **must** provide an **end-user friendly** description for your change below
## Summary of PR
**End-user friendly description of the problem this fixes or functionality this introduces.**
<!-- Summarize what the PR does -->
## Demo Screenshots/Videos
---
**Summarize what the PR does, explaining any non-trivial design decisions.**
<!-- AI/LLM AGENTS: This section is intended for a human author to add screenshots or videos demonstrating the PR in action (optional). While many pull requests may be generated by AI/LLM agents, we are fine with this as long as a human author has reviewed and tested the changes to ensure accuracy and functionality. -->
## Change Type
<!-- Choose the types that apply to your PR -->
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Refactor
- [ ] Other (dependency update, docs, typo fixes, etc.)
## Checklist
<!-- AI/LLM AGENTS: This checklist is for a human author to complete. Do NOT check either of the two boxes below. Leave them unchecked until a human has personally reviewed and tested the changes. -->
- [ ] I have read and reviewed the code and I understand what the code is doing.
- [ ] I have tested the code to the best of my ability and ensured it works as expected.
## Fixes
<!-- If this resolves an issue, link it here so it will close automatically upon merge. -->
Resolves #(issue)
## Release Notes
<!-- Check the box if this change is worth adding to the release notes. If checked, you must provide an
end-user friendly description for your change below the checkbox. -->
- [ ] Include this change in the Release Notes.
---
**Link of any specific issues this addresses:**

73
.github/scripts/check_version_consistency.py vendored Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
import os
import re
import sys
def find_version_references(directory: str) -> tuple[set[str], set[str]]:
openhands_versions = set()
runtime_versions = set()
version_pattern_openhands = re.compile(r'openhands:(\d{1})\.(\d{2})')
version_pattern_runtime = re.compile(r'runtime:(\d{1})\.(\d{2})')
for root, _, files in os.walk(directory):
# Skip .git directory and docs/build directory
if '.git' in root or 'docs/build' in root:
continue
for file in files:
if file.endswith(
('.md', '.yml', '.yaml', '.txt', '.html', '.py', '.js', '.ts')
):
file_path = os.path.join(root, file)
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Find all openhands version references
matches = version_pattern_openhands.findall(content)
if matches:
print(f'Found openhands version {matches} in {file_path}')
openhands_versions.update(matches)
# Find all runtime version references
matches = version_pattern_runtime.findall(content)
if matches:
print(f'Found runtime version {matches} in {file_path}')
runtime_versions.update(matches)
except Exception as e:
print(f'Error reading {file_path}: {e}', file=sys.stderr)
return openhands_versions, runtime_versions
def main():
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
print(f'Checking version consistency in {repo_root}')
openhands_versions, runtime_versions = find_version_references(repo_root)
print(f'Found openhands versions: {sorted(openhands_versions)}')
print(f'Found runtime versions: {sorted(runtime_versions)}')
exit_code = 0
if len(openhands_versions) > 1:
print('Error: Multiple openhands versions found:', file=sys.stderr)
print('Found versions:', sorted(openhands_versions), file=sys.stderr)
exit_code = 1
elif len(openhands_versions) == 0:
print('Warning: No openhands version references found', file=sys.stderr)
if len(runtime_versions) > 1:
print('Error: Multiple runtime versions found:', file=sys.stderr)
print('Found versions:', sorted(runtime_versions), file=sys.stderr)
exit_code = 1
elif len(runtime_versions) == 0:
print('Warning: No runtime version references found', file=sys.stderr)
sys.exit(exit_code)
if __name__ == '__main__':
main()

View File

@@ -1,330 +0,0 @@
#!/usr/bin/env python3
"""
Find all PRs that went in between two commits in the OpenHands/OpenHands repository.
Handles cherry-picks and different merge strategies.
This script is designed to run from within the OpenHands repository under .github/scripts:
.github/scripts/find_prs_between_commits.py
Usage: find_prs_between_commits <older_commit> <newer_commit> [--repo <path>]
"""
import json
import os
import re
import subprocess
import sys
from collections import defaultdict
from pathlib import Path
from typing import Optional
def find_openhands_repo() -> Optional[Path]:
"""
Find the OpenHands repository.
Since this script is designed to live in .github/scripts/, it assumes
the repository root is two levels up from the script location.
Tries:
1. Repository root (../../ from script location)
2. Current directory
3. Environment variable OPENHANDS_REPO
"""
# Check repository root (assuming script is in .github/scripts/)
script_dir = Path(__file__).parent.absolute()
repo_root = (
script_dir.parent.parent
) # Go up two levels: scripts -> .github -> repo root
if (repo_root / '.git').exists():
return repo_root
# Check current directory
if (Path.cwd() / '.git').exists():
return Path.cwd()
# Check environment variable
if 'OPENHANDS_REPO' in os.environ:
repo_path = Path(os.environ['OPENHANDS_REPO'])
if (repo_path / '.git').exists():
return repo_path
return None
def run_git_command(cmd: list[str], repo_path: Path) -> str:
"""Run a git command in the repository directory and return its output."""
try:
result = subprocess.run(
cmd, capture_output=True, text=True, check=True, cwd=str(repo_path)
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
print(f'Error running git command: {" ".join(cmd)}', file=sys.stderr)
print(f'Error: {e.stderr}', file=sys.stderr)
sys.exit(1)
def extract_pr_numbers_from_message(message: str) -> set[int]:
"""Extract PR numbers from commit message in any common format."""
# Match #12345 anywhere, including in patterns like (#12345) or "Merge pull request #12345"
matches = re.findall(r'#(\d+)', message)
return set(int(m) for m in matches)
def get_commit_info(commit_hash: str, repo_path: Path) -> tuple[str, str, str]:
"""Get commit subject, body, and author from a commit hash."""
subject = run_git_command(
['git', 'log', '-1', '--format=%s', commit_hash], repo_path
)
body = run_git_command(['git', 'log', '-1', '--format=%b', commit_hash], repo_path)
author = run_git_command(
['git', 'log', '-1', '--format=%an <%ae>', commit_hash], repo_path
)
return subject, body, author
def get_commits_between(
older_commit: str, newer_commit: str, repo_path: Path
) -> list[str]:
"""Get all commit hashes between two commits."""
commits_output = run_git_command(
['git', 'rev-list', f'{older_commit}..{newer_commit}'], repo_path
)
if not commits_output:
return []
return commits_output.split('\n')
def get_pr_info_from_github(pr_number: int, repo_path: Path) -> Optional[dict]:
"""Get PR information from GitHub API if GITHUB_TOKEN is available."""
try:
# Set up environment with GitHub token
env = os.environ.copy()
if 'GITHUB_TOKEN' in env:
env['GH_TOKEN'] = env['GITHUB_TOKEN']
result = subprocess.run(
[
'gh',
'pr',
'view',
str(pr_number),
'--json',
'number,title,author,mergedAt,baseRefName,headRefName,url',
],
capture_output=True,
text=True,
check=True,
env=env,
cwd=str(repo_path),
)
return json.loads(result.stdout)
except (subprocess.CalledProcessError, FileNotFoundError, json.JSONDecodeError):
return None
def find_prs_between_commits(
older_commit: str, newer_commit: str, repo_path: Path
) -> dict[int, dict]:
"""
Find all PRs that went in between two commits.
Returns a dictionary mapping PR numbers to their information.
"""
print(f'Repository: {repo_path}', file=sys.stderr)
print('Finding PRs between commits:', file=sys.stderr)
print(f' Older: {older_commit}', file=sys.stderr)
print(f' Newer: {newer_commit}', file=sys.stderr)
print(file=sys.stderr)
# Verify commits exist
try:
run_git_command(['git', 'rev-parse', '--verify', older_commit], repo_path)
run_git_command(['git', 'rev-parse', '--verify', newer_commit], repo_path)
except SystemExit:
print('Error: One or both commits not found in repository', file=sys.stderr)
sys.exit(1)
# Extract PRs from the older commit itself (to exclude from results)
# These PRs are already included at or before the older commit
older_subject, older_body, _ = get_commit_info(older_commit, repo_path)
older_message = f'{older_subject}\n{older_body}'
excluded_prs = extract_pr_numbers_from_message(older_message)
if excluded_prs:
print(
f'Excluding PRs already in older commit: {", ".join(f"#{pr}" for pr in sorted(excluded_prs))}',
file=sys.stderr,
)
print(file=sys.stderr)
# Get all commits between the two
commits = get_commits_between(older_commit, newer_commit, repo_path)
print(f'Found {len(commits)} commits to analyze', file=sys.stderr)
print(file=sys.stderr)
# Extract PR numbers from all commits
pr_info: dict[int, dict] = {}
commits_by_pr: dict[int, list[str]] = defaultdict(list)
for commit_hash in commits:
subject, body, author = get_commit_info(commit_hash, repo_path)
full_message = f'{subject}\n{body}'
pr_numbers = extract_pr_numbers_from_message(full_message)
for pr_num in pr_numbers:
# Skip PRs that are already in the older commit
if pr_num in excluded_prs:
continue
commits_by_pr[pr_num].append(commit_hash)
if pr_num not in pr_info:
pr_info[pr_num] = {
'number': pr_num,
'first_commit': commit_hash[:8],
'first_commit_subject': subject,
'commits': [],
'github_info': None,
}
pr_info[pr_num]['commits'].append(
{'hash': commit_hash[:8], 'subject': subject, 'author': author}
)
# Try to get additional info from GitHub API
print('Fetching additional info from GitHub API...', file=sys.stderr)
for pr_num in pr_info.keys():
github_info = get_pr_info_from_github(pr_num, repo_path)
if github_info:
pr_info[pr_num]['github_info'] = github_info
print(file=sys.stderr)
return pr_info
def print_results(pr_info: dict[int, dict]):
"""Print the results in a readable format."""
sorted_prs = sorted(pr_info.items(), key=lambda x: x[0])
print(f'{"=" * 80}')
print(f'Found {len(sorted_prs)} PRs')
print(f'{"=" * 80}')
print()
for pr_num, info in sorted_prs:
print(f'PR #{pr_num}')
if info['github_info']:
gh = info['github_info']
print(f' Title: {gh["title"]}')
print(f' Author: {gh["author"]["login"]}')
print(f' URL: {gh["url"]}')
if gh.get('mergedAt'):
print(f' Merged: {gh["mergedAt"]}')
if gh.get('baseRefName'):
print(f' Base: {gh["baseRefName"]}{gh["headRefName"]}')
else:
print(f' Subject: {info["first_commit_subject"]}')
# Show if this PR has multiple commits (cherry-picked or multiple commits)
commit_count = len(info['commits'])
if commit_count > 1:
print(
f' ⚠️ Found {commit_count} commits (possible cherry-pick or multi-commit PR):'
)
for commit in info['commits'][:3]: # Show first 3
print(f' {commit["hash"]}: {commit["subject"][:60]}')
if commit_count > 3:
print(f' ... and {commit_count - 3} more')
else:
print(f' Commit: {info["first_commit"]}')
print()
def main():
if len(sys.argv) < 3:
print('Usage: find_prs_between_commits <older_commit> <newer_commit> [options]')
print()
print('Arguments:')
print(' <older_commit> The older commit hash (or ref)')
print(' <newer_commit> The newer commit hash (or ref)')
print()
print('Options:')
print(' --json Output results in JSON format')
print(' --repo <path> Path to OpenHands repository (default: auto-detect)')
print()
print('Example:')
print(
' find_prs_between_commits c79e0cd3c7a2501a719c9296828d7a31e4030585 35bddb14f15124a3dc448a74651a6592911d99e9'
)
print()
print('Repository Detection:')
print(' The script will try to find the OpenHands repository in this order:')
print(' 1. --repo argument')
print(' 2. Repository root (../../ from script location)')
print(' 3. Current directory')
print(' 4. OPENHANDS_REPO environment variable')
print()
print('Environment variables:')
print(
' GITHUB_TOKEN Optional. If set, will fetch additional PR info from GitHub API'
)
print(' OPENHANDS_REPO Optional. Path to OpenHands repository')
sys.exit(1)
older_commit = sys.argv[1]
newer_commit = sys.argv[2]
json_output = '--json' in sys.argv
# Check for --repo argument
repo_path = None
if '--repo' in sys.argv:
repo_idx = sys.argv.index('--repo')
if repo_idx + 1 < len(sys.argv):
repo_path = Path(sys.argv[repo_idx + 1])
if not (repo_path / '.git').exists():
print(f'Error: {repo_path} is not a git repository', file=sys.stderr)
sys.exit(1)
# Auto-detect repository if not specified
if repo_path is None:
repo_path = find_openhands_repo()
if repo_path is None:
print('Error: Could not find OpenHands repository', file=sys.stderr)
print('Please either:', file=sys.stderr)
print(
' 1. Place this script in .github/scripts/ within the OpenHands repository',
file=sys.stderr,
)
print(' 2. Run from the OpenHands repository directory', file=sys.stderr)
print(
' 3. Use --repo <path> to specify the repository location',
file=sys.stderr,
)
print(' 4. Set OPENHANDS_REPO environment variable', file=sys.stderr)
sys.exit(1)
# Find PRs
pr_info = find_prs_between_commits(older_commit, newer_commit, repo_path)
if json_output:
# Output as JSON
print(json.dumps(pr_info, indent=2))
else:
# Print results in human-readable format
print_results(pr_info)
# Also print a simple list for easy copying
print(f'{"=" * 80}')
print('PR Numbers (for easy copying):')
print(f'{"=" * 80}')
sorted_pr_nums = sorted(pr_info.keys())
print(', '.join(f'#{pr}' for pr in sorted_pr_nums))
if __name__ == '__main__':
main()

View File

@@ -1,58 +0,0 @@
#!/bin/bash
set -euxo pipefail
# This script updates the PR description with commands to run the PR locally
# It adds both Docker and uvx commands
# Get the branch name for the PR
BRANCH_NAME=$(gh pr view "$PR_NUMBER" --json headRefName --jq .headRefName)
# Define the Docker command
DOCKER_RUN_COMMAND="docker run -it --rm \
-p 3000:3000 \
-v /var/run/docker.sock:/var/run/docker.sock \
--add-host host.docker.internal:host-gateway \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.openhands.dev/openhands/runtime:${SHORT_SHA}-nikolaik \
--name openhands-app-${SHORT_SHA} \
docker.openhands.dev/openhands/openhands:${SHORT_SHA}"
# Get the current PR body
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq .body)
# Prepare the new PR body with both commands
if echo "$PR_BODY" | grep -q "To run this PR locally, use the following command:"; then
# For existing PR descriptions, use a more robust approach
# Split the PR body at the "To run this PR locally" section and replace everything after it
BEFORE_SECTION=$(echo "$PR_BODY" | sed '/To run this PR locally, use the following command:/,$d')
NEW_PR_BODY=$(cat <<EOF
${BEFORE_SECTION}
To run this PR locally, use the following command:
GUI with Docker:
\`\`\`
${DOCKER_RUN_COMMAND}
\`\`\`
EOF
)
else
# For new PR descriptions: use heredoc safely without indentation
NEW_PR_BODY=$(cat <<EOF
$PR_BODY
---
To run this PR locally, use the following command:
GUI with Docker:
\`\`\`
${DOCKER_RUN_COMMAND}
\`\`\`
EOF
)
fi
# Update the PR description
echo "Updating PR description with Docker and uvx commands"
gh pr edit "$PR_NUMBER" --body "$NEW_PR_BODY"

View File

@@ -1,65 +0,0 @@
name: Check Package Versions
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
check-package-versions:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Check for any 'rev' fields in pyproject.toml
run: |
python - <<'PY'
import sys, tomllib, pathlib
path = pathlib.Path("pyproject.toml")
if not path.exists():
print("❌ ERROR: pyproject.toml not found")
sys.exit(1)
try:
data = tomllib.loads(path.read_text(encoding="utf-8"))
except Exception as e:
print(f"❌ ERROR: Failed to parse pyproject.toml: {e}")
sys.exit(1)
poetry = data.get("tool", {}).get("poetry", {})
sections = {
"dependencies": poetry.get("dependencies", {}),
}
errors = []
print("🔍 Checking for any dependencies with 'rev' fields...\n")
for section_name, deps in sections.items():
if not isinstance(deps, dict):
continue
for pkg_name, cfg in deps.items():
if isinstance(cfg, dict) and "rev" in cfg:
msg = f" ✖ {pkg_name} in [{section_name}] uses rev='{cfg['rev']}' (NOT ALLOWED)"
print(msg)
errors.append(msg)
else:
print(f" • {pkg_name}: OK")
if errors:
print("\n❌ FAILED: Found dependencies using 'rev' fields:\n" + "\n".join(errors))
print("\nPlease use versioned releases instead, e.g.:")
print(' my-package = "1.0.0"')
sys.exit(1)
print("\n✅ SUCCESS: No 'rev' fields found. All dependencies are using proper versioned releases.")
PY

View File

@@ -1,122 +0,0 @@
name: Check Version Consistency
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
check-version-consistency:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Check version and Docker image tag consistency
run: |
python - <<'PY'
import json
import re
import sys
import tomllib
errors = []
warnings = []
# ── 1. Extract the canonical version from pyproject.toml ──────────
with open("pyproject.toml", "rb") as f:
pyproject = tomllib.load(f)
version = pyproject["tool"]["poetry"]["version"]
major_minor = ".".join(version.split(".")[:2])
print(f"📦 pyproject.toml version: {version} (major.minor: {major_minor})")
# ── 2. Check frontend/package.json ────────────────────────────────
with open("frontend/package.json") as f:
pkg = json.load(f)
if pkg["version"] != version:
errors.append(
f"frontend/package.json version is '{pkg['version']}', expected '{version}'"
)
else:
print(f" ✔ frontend/package.json: {pkg['version']}")
# ── 3. Check frontend/package-lock.json (2 places) ───────────────
with open("frontend/package-lock.json") as f:
lock = json.load(f)
for key, val in [
("root.version", lock.get("version")),
('packages[""].version', lock.get("packages", {}).get("", {}).get("version")),
]:
if val != version:
errors.append(
f"frontend/package-lock.json {key} is '{val}', expected '{version}'"
)
else:
print(f" ✔ frontend/package-lock.json {key}: {val}")
# ── 4. Check compose files use agent-server images ─────────────────
# Both compose files should use ghcr.io/.../agent-server (not runtime).
# Agent-server tags use SDK version (e.g. "1.12.0-python") or commit
# hashes (e.g. "31536c8-python") — both are acceptable.
repo_pattern = re.compile(r"AGENT_SERVER_IMAGE_REPOSITORY[^}]*:-([^}]+)")
tag_pattern = re.compile(r"AGENT_SERVER_IMAGE_TAG:-([^}]+)")
for filepath in ["docker-compose.yml", "containers/dev/compose.yml"]:
try:
with open(filepath) as f:
content = f.read()
except FileNotFoundError:
warnings.append(f"{filepath}: file not found")
continue
repos = repo_pattern.findall(content)
tags = tag_pattern.findall(content)
if not repos:
warnings.append(f"{filepath}: no AGENT_SERVER_IMAGE_REPOSITORY default found")
else:
repo = repos[0]
if "agent-server" not in repo:
errors.append(
f"{filepath}: AGENT_SERVER_IMAGE_REPOSITORY defaults to '{repo}', "
f"expected an agent-server image (not runtime)"
)
else:
print(f" ✔ {filepath} image repository: {repo}")
if not tags:
warnings.append(f"{filepath}: no AGENT_SERVER_IMAGE_TAG default found")
else:
tag = tags[0]
if not tag:
errors.append(f"{filepath}: AGENT_SERVER_IMAGE_TAG default is empty")
else:
print(f" ✔ {filepath} image tag: {tag}")
# ── 5. Report ─────────────────────────────────────────────────────
print()
if warnings:
print("⚠ Warnings:")
for w in warnings:
print(f" {w}")
print()
if errors:
print("❌ FAILED: Version inconsistencies found:\n")
for e in errors:
print(f" ✖ {e}")
print(
"\nAll version numbers and Docker image tags must be consistent."
"\nSee .agents/skills/update-sdk/SKILL.md for the full checklist."
)
sys.exit(1)
else:
print("✅ All version numbers and Docker image tags are consistent.")
PY

69
.github/workflows/clean-up.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
# Workflow that cleans up outdated and old workflows to prevent out of disk issues
name: Delete old workflow runs
# This workflow is currently only triggered manually
on:
workflow_dispatch:
inputs:
days:
description: 'Days-worth of runs to keep for each workflow'
required: true
default: '30'
minimum_runs:
description: 'Minimum runs to keep for each workflow'
required: true
default: '10'
delete_workflow_pattern:
description: 'Name or filename of the workflow (if not set, all workflows are targeted)'
required: false
delete_workflow_by_state_pattern:
description: 'Filter workflows by state: active, deleted, disabled_fork, disabled_inactivity, disabled_manually'
required: true
default: "ALL"
type: choice
options:
- "ALL"
- active
- deleted
- disabled_inactivity
- disabled_manually
delete_run_by_conclusion_pattern:
description: 'Remove runs based on conclusion: action_required, cancelled, failure, skipped, success'
required: true
default: 'ALL'
type: choice
options:
- 'ALL'
- 'Unsuccessful: action_required,cancelled,failure,skipped'
- action_required
- cancelled
- failure
- skipped
- success
dry_run:
description: 'Logs simulated changes, no deletions are performed'
required: false
jobs:
del_runs:
runs-on: blacksmith-4vcpu-ubuntu-2204
permissions:
actions: write
contents: read
steps:
- name: Delete workflow runs
uses: Mattraks/delete-workflow-runs@v2
with:
token: ${{ github.token }}
repository: ${{ github.repository }}
retain_days: ${{ github.event.inputs.days }}
keep_minimum_runs: ${{ github.event.inputs.minimum_runs }}
delete_workflow_pattern: ${{ github.event.inputs.delete_workflow_pattern }}
delete_workflow_by_state_pattern: ${{ github.event.inputs.delete_workflow_by_state_pattern }}
delete_run_by_conclusion_pattern: >-
${{
startsWith(github.event.inputs.delete_run_by_conclusion_pattern, 'Unsuccessful:')
&& 'action_required,cancelled,failure,skipped'
|| github.event.inputs.delete_run_by_conclusion_pattern
}}
dry_run: ${{ github.event.inputs.dry_run }}

View File

@@ -1,228 +0,0 @@
name: End-to-End Tests
on:
pull_request:
types: [opened, synchronize, reopened, labeled]
branches:
- main
- develop
workflow_dispatch:
jobs:
e2e-tests:
if: contains(github.event.pull_request.labels.*.name, 'end-to-end') || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 60
env:
GITHUB_REPO_NAME: ${{ github.repository }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install poetry via pipx
uses: abatilo/actions-poetry@v4
with:
poetry-version: 2.1.3
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: 'poetry'
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-0 libnotify4 libnss3 libxss1 libxtst6 xauth xvfb libgbm1 libasound2t64 netcat-openbsd
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: 'frontend/package-lock.json'
- name: Setup environment for end-to-end tests
run: |
# Create test results directory
mkdir -p test-results
# Create downloads directory for OpenHands (use a directory in the home folder)
mkdir -p $HOME/downloads
sudo chown -R $USER:$USER $HOME/downloads
sudo chmod -R 755 $HOME/downloads
- name: Build OpenHands
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LLM_MODEL: ${{ secrets.LLM_MODEL || 'gpt-4o' }}
LLM_API_KEY: ${{ secrets.LLM_API_KEY || 'test-key' }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
INSTALL_DOCKER: 1
RUNTIME: docker
FRONTEND_PORT: 12000
FRONTEND_HOST: 0.0.0.0
BACKEND_HOST: 0.0.0.0
BACKEND_PORT: 3000
ENABLE_BROWSER: true
INSTALL_PLAYWRIGHT: 1
run: |
# Fix poetry.lock file if needed
echo "Fixing poetry.lock file if needed..."
poetry lock
# Build OpenHands using make build
echo "Running make build..."
make build
# Install Chromium Headless Shell for Playwright (needed for pytest-playwright)
echo "Installing Chromium Headless Shell for Playwright..."
poetry run playwright install chromium-headless-shell
# Verify Playwright browsers are installed (for e2e tests only)
echo "Verifying Playwright browsers installation for e2e tests..."
BROWSER_CHECK=$(poetry run python tests/e2e/check_playwright.py 2>/dev/null)
if [ "$BROWSER_CHECK" != "chromium_found" ]; then
echo "ERROR: Chromium browser not found or not working for e2e tests"
echo "$BROWSER_CHECK"
exit 1
else
echo "Playwright browsers are properly installed for e2e tests."
fi
# Docker runtime will handle workspace directory creation
# Start the application using make run with custom parameters and reduced logging
echo "Starting OpenHands using make run..."
# Set environment variables to reduce logging verbosity
export PYTHONUNBUFFERED=1
export LOG_LEVEL=WARNING
export UVICORN_LOG_LEVEL=warning
export OPENHANDS_LOG_LEVEL=WARNING
FRONTEND_PORT=12000 FRONTEND_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0 make run > /tmp/openhands-e2e-test.log 2>&1 &
# Store the PID of the make run process
MAKE_PID=$!
echo "OpenHands started with PID: $MAKE_PID"
# Wait for the application to start
echo "Waiting for OpenHands to start..."
max_attempts=15
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Checking if OpenHands is running (attempt $attempt of $max_attempts)..."
# Check if the process is still running
if ! ps -p $MAKE_PID > /dev/null; then
echo "ERROR: OpenHands process has terminated unexpectedly"
echo "Last 50 lines of the log:"
tail -n 50 /tmp/openhands-e2e-test.log
exit 1
fi
# Check if frontend port is open
if nc -z localhost 12000; then
# Verify we can get HTML content
if curl -s http://localhost:12000 | grep -q "<html"; then
echo "SUCCESS: OpenHands is running and serving HTML content on port 12000"
break
else
echo "Port 12000 is open but not serving HTML content yet"
fi
else
echo "Frontend port 12000 is not open yet"
fi
# Show log output on each attempt
echo "Recent log output:"
tail -n 20 /tmp/openhands-e2e-test.log
# Wait before next attempt
echo "Waiting 10 seconds before next check..."
sleep 10
attempt=$((attempt + 1))
# Exit if we've reached the maximum number of attempts
if [ $attempt -gt $max_attempts ]; then
echo "ERROR: OpenHands failed to start after $max_attempts attempts"
echo "Last 50 lines of the log:"
tail -n 50 /tmp/openhands-e2e-test.log
exit 1
fi
done
# Final verification that the app is running
if ! nc -z localhost 12000 || ! curl -s http://localhost:12000 | grep -q "<html"; then
echo "ERROR: OpenHands is not running properly on port 12000"
echo "Last 50 lines of the log:"
tail -n 50 /tmp/openhands-e2e-test.log
exit 1
fi
# Print success message
echo "OpenHands is running successfully on port 12000"
- name: Run end-to-end tests
env:
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN }}
LLM_MODEL: ${{ secrets.LLM_MODEL || 'gpt-4o' }}
LLM_API_KEY: ${{ secrets.LLM_API_KEY || 'test-key' }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
run: |
# Check if the application is running
if ! nc -z localhost 12000; then
echo "ERROR: OpenHands is not running on port 12000"
echo "Last 50 lines of the log:"
tail -n 50 /tmp/openhands-e2e-test.log
exit 1
fi
# Run the tests with detailed output
cd tests/e2e
poetry run python -m pytest \
test_settings.py::test_github_token_configuration \
test_conversation.py::test_conversation_start \
test_browsing_catchphrase.py::test_browsing_catchphrase \
test_multi_conversation_resume.py::test_multi_conversation_resume \
-v --no-header --capture=no --timeout=900
- name: Upload test results
if: always()
uses: actions/upload-artifact@v6
with:
name: playwright-report
path: tests/e2e/test-results/
retention-days: 30
- name: Upload OpenHands logs
if: always()
uses: actions/upload-artifact@v6
with:
name: openhands-logs
path: |
/tmp/openhands-e2e-test.log
/tmp/openhands-e2e-build.log
/tmp/openhands-backend.log
/tmp/openhands-frontend.log
/tmp/backend-health-check.log
/tmp/frontend-check.log
/tmp/vite-config.log
/tmp/makefile-contents.log
retention-days: 30
- name: Cleanup
if: always()
run: |
# Stop OpenHands processes
echo "Stopping OpenHands processes..."
pkill -f "python -m openhands.server" || true
pkill -f "npm run dev" || true
pkill -f "make run" || true
# Print process status for debugging
echo "Checking if any OpenHands processes are still running:"
ps aux | grep -E "openhands|npm run dev" || true

View File

@@ -1,52 +0,0 @@
name: Enterprise Check Migrations
on:
pull_request:
paths:
- 'enterprise/migrations/**'
jobs:
check-sync:
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Fetch base branch
run: git fetch origin ${{ github.event.pull_request.base.ref }}
- name: Check if base branch is ancestor of PR
id: check_up_to_date
shell: bash
run: |
BASE="origin/${{ github.event.pull_request.base.ref }}"
HEAD="${{ github.event.pull_request.head.sha }}"
if git merge-base --is-ancestor "$BASE" "$HEAD"; then
echo "We're up to date with base $BASE"
exit 0
else
echo "NOT up to date with base $BASE"
exit 1
fi
- name: Find Comment
uses: peter-evans/find-comment@v3
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: |
⚠️ This PR contains **migrations**
- name: Comment warning on PR
uses: peter-evans/create-or-update-comment@v5
with:
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
edit-mode: replace
body: |
⚠️ This PR contains **migrations**. Please synchronize before merging to prevent conflicts.

View File

@@ -1,47 +0,0 @@
# Workflow that runs frontend e2e tests with Playwright
name: Run Frontend E2E Tests
on:
push:
branches:
- main
pull_request:
paths:
- "frontend/**"
- ".github/workflows/fe-e2e-tests.yml"
concurrency:
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
cancel-in-progress: true
jobs:
fe-e2e-test:
name: FE E2E Tests
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
node-version: [22]
fail-fast: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: useblacksmith/setup-node@v5
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
working-directory: ./frontend
run: npm ci
- name: Install Playwright browsers
working-directory: ./frontend
run: npx playwright install --with-deps chromium
- name: Run Playwright tests
working-directory: ./frontend
run: npx playwright test --project=chromium
- name: Upload Playwright report
uses: actions/upload-artifact@v6
if: always()
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 30

View File

@@ -9,8 +9,8 @@ on:
- main
pull_request:
paths:
- "frontend/**"
- ".github/workflows/fe-unit-tests.yml"
- 'frontend/**'
- '.github/workflows/fe-unit-tests.yml'
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
concurrency:
@@ -24,7 +24,7 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
node-version: [22]
node-version: [20, 22]
fail-fast: true
steps:
- name: Checkout
@@ -38,7 +38,7 @@ jobs:
run: npm ci
- name: Run TypeScript compilation
working-directory: ./frontend
run: npm run build
run: npm run make-i18n && tsc
- name: Run tests and collect coverage
working-directory: ./frontend
run: npm run test:coverage

View File

@@ -9,16 +9,15 @@ on:
push:
branches:
- main
- "saas-rel-*"
tags:
- "*"
- '*'
pull_request:
workflow_dispatch:
inputs:
reason:
description: "Reason for manual trigger"
description: 'Reason for manual trigger'
required: true
default: ""
default: ''
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
concurrency:
@@ -38,9 +37,12 @@ jobs:
shell: bash
id: define-base-images
run: |
# Only build nikolaik on PRs, otherwise build both nikolaik and ubuntu.
if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
json=$(jq -n -c '[
{ image: "nikolaik/python-nodejs:python3.12-nodejs22", tag: "nikolaik" }
{ image: "nikolaik/python-nodejs:python3.12-nodejs22", tag: "nikolaik" },
{ image: "ubuntu:24.04", tag: "ubuntu" }
]')
else
json=$(jq -n -c '[
@@ -54,17 +56,19 @@ jobs:
ghcr_build_app:
name: Build App Image
runs-on: blacksmith-4vcpu-ubuntu-2204
if: "!(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/ext-v'))"
permissions:
contents: read
packages: write
outputs:
# Since this job uses outputs it cannot use matrix
hash_from_app_image: ${{ steps.get_hash_in_app_image.outputs.hash_from_app_image }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.7.0
uses: docker/setup-qemu-action@v3.6.0
with:
image: tonistiigi/binfmt:latest
- name: Login to GHCR
@@ -83,12 +87,24 @@ jobs:
if: "!github.event.pull_request.head.repo.fork"
run: |
./containers/build.sh -i openhands -o ${{ env.REPO_OWNER }} --push
- name: Build app image
if: "github.event.pull_request.head.repo.fork"
run: |
./containers/build.sh -i openhands -o ${{ env.REPO_OWNER }} --load
- name: Get hash in App Image
id: get_hash_in_app_image
run: |
# Run the build script in the app image
docker run -e SANDBOX_USER_ID=0 -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/${{ env.REPO_OWNER }}/openhands:${{ env.RELEVANT_SHA }} /bin/bash -c "mkdir -p containers/runtime; python3 openhands/runtime/utils/runtime_build.py --base_image ${{ env.BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST }} --build_folder containers/runtime --force_rebuild" 2>&1 | tee docker-outputs.txt
# Get the hash from the build script
hash_from_app_image=$(cat docker-outputs.txt | grep "Hash for docker build directory" | awk -F "): " '{print $2}' | uniq | head -n1)
echo "hash_from_app_image=$hash_from_app_image" >> $GITHUB_OUTPUT
echo "Hash from app image: $hash_from_app_image"
# Builds the runtime Docker images
ghcr_build_runtime:
name: Build Runtime Image
runs-on: blacksmith-8vcpu-ubuntu-2204
if: "!(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/ext-v'))"
name: Build Image
runs-on: blacksmith-4vcpu-ubuntu-2204
permissions:
contents: read
packages: write
@@ -102,7 +118,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.7.0
uses: docker/setup-qemu-action@v3.6.0
with:
image: tonistiigi/binfmt:latest
- name: Login to GHCR
@@ -114,17 +130,26 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: "3.12"
cache: poetry
python-version: '3.12'
- name: Cache Poetry dependencies
uses: useblacksmith/cache@v5
with:
path: |
~/.cache/pypoetry
~/.virtualenvs
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
# This is the one that saves the cache, the others set 'lookup-only: true'
restore-keys: |
${{ runner.os }}-poetry-
- name: Install poetry via pipx
run: pipx install poetry
- name: Install Python dependencies using Poetry
run: make install-python-dependencies POETRY_GROUP=main INSTALL_PLAYWRIGHT=0
- name: Create source distribution and Dockerfile
run: poetry run python3 -m openhands.runtime.utils.runtime_build --base_image ${{ matrix.base_image.image }} --build_folder containers/runtime --force_rebuild
run: poetry run python3 openhands/runtime/utils/runtime_build.py --base_image ${{ matrix.base_image.image }} --build_folder containers/runtime --force_rebuild
- name: Lowercase Repository Owner
run: |
echo REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
@@ -135,7 +160,6 @@ jobs:
if: github.event.pull_request.head.repo.fork != true
shell: bash
run: |
./containers/build.sh -i runtime -o ${{ env.REPO_OWNER }} -t ${{ matrix.base_image.tag }} --dry
DOCKER_BUILD_JSON=$(jq -c . < docker-build-dry.json)
@@ -149,9 +173,6 @@ jobs:
push: true
tags: ${{ env.DOCKER_TAGS }}
platforms: ${{ env.DOCKER_PLATFORM }}
# Caching directives to boost performance
cache-from: type=registry,ref=ghcr.io/${{ env.REPO_OWNER }}/runtime:buildcache-${{ matrix.base_image.tag }}
cache-to: type=registry,ref=ghcr.io/${{ env.REPO_OWNER }}/runtime:buildcache-${{ matrix.base_image.tag }},mode=max
build-args: ${{ env.DOCKER_BUILD_ARGS }}
context: containers/runtime
provenance: false
@@ -164,91 +185,227 @@ jobs:
context: containers/runtime
- name: Upload runtime source for fork
if: github.event.pull_request.head.repo.fork
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: runtime-src-${{ matrix.base_image.tag }}
path: containers/runtime
ghcr_build_enterprise:
name: Push Enterprise Image
runs-on: blacksmith-8vcpu-ubuntu-2204
permissions:
contents: read
packages: write
needs: [define-matrix, ghcr_build_app]
# Do not build enterprise in forks
if: github.event.pull_request.head.repo.fork != true
verify_hash_equivalence_in_runtime_and_app:
name: Verify Hash Equivalence in Runtime and Docker images
runs-on: blacksmith-4vcpu-ubuntu-2204
needs: [ghcr_build_runtime, ghcr_build_app]
strategy:
fail-fast: false
matrix:
base_image: ['nikolaik']
env:
BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST: nikolaik/python-nodejs:python3.12-nodejs22
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
# Set up Docker Buildx for better performance
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Poetry dependencies
uses: useblacksmith/cache@v5
with:
driver-opts: network=host
- name: Login to GHCR
uses: docker/login-action@v3
path: |
~/.cache/pypoetry
~/.virtualenvs
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
lookup-only: true
restore-keys: |
${{ runner.os }}-poetry-
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/openhands/enterprise-server
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha
type=sha,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
flavor: |
latest=auto
prefix=
suffix=
env:
DOCKER_METADATA_PR_HEAD_SHA: true
- name: Determine app image tag
shell: bash
python-version: '3.12'
- name: Install poetry via pipx
run: pipx install poetry
- name: Install Python dependencies using Poetry
run: make install-python-dependencies POETRY_GROUP=main INSTALL_PLAYWRIGHT=0
- name: Get hash in App Image
run: |
# Duplicated with build.sh
sanitized_ref_name=$(echo "$GITHUB_REF_NAME" | sed 's/[^a-zA-Z0-9.-]\+/-/g')
OPENHANDS_BUILD_VERSION=$sanitized_ref_name
sanitized_ref_name=$(echo "$sanitized_ref_name" | tr '[:upper:]' '[:lower:]') # lower case is required in tagging
echo "OPENHANDS_DOCKER_TAG=${sanitized_ref_name}" >> $GITHUB_ENV
- name: Build and push Docker image
echo "Hash from app image: ${{ needs.ghcr_build_app.outputs.hash_from_app_image }}"
echo "hash_from_app_image=${{ needs.ghcr_build_app.outputs.hash_from_app_image }}" >> $GITHUB_ENV
- name: Get hash using code (development mode)
run: |
mkdir -p containers/runtime
poetry run python3 openhands/runtime/utils/runtime_build.py --base_image ${{ env.BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST }} --build_folder containers/runtime --force_rebuild > output.txt 2>&1
hash_from_code=$(cat output.txt | grep "Hash for docker build directory" | awk -F "): " '{print $2}' | uniq | head -n1)
echo "hash_from_code=$hash_from_code" >> $GITHUB_ENV
- name: Compare hashes
run: |
echo "Hash from App Image: ${{ env.hash_from_app_image }}"
echo "Hash from Code: ${{ env.hash_from_code }}"
if [ "${{ env.hash_from_app_image }}" = "${{ env.hash_from_code }}" ]; then
echo "Hashes match!"
else
echo "Hashes do not match!"
exit 1
fi
# Run unit tests with the Docker runtime Docker images as root
test_runtime_root:
name: RT Unit Tests (Root)
needs: [ghcr_build_runtime, define-matrix]
runs-on: blacksmith-8vcpu-ubuntu-2204
strategy:
fail-fast: false
matrix:
base_image: ${{ fromJson(needs.define-matrix.outputs.base_image) }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Download runtime source for fork
if: github.event.pull_request.head.repo.fork
uses: actions/download-artifact@v4
with:
name: runtime-src-${{ matrix.base_image.tag }}
path: containers/runtime
- name: Lowercase Repository Owner
run: |
echo REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
# Forked repos can't push to GHCR, so we need to rebuild using cache
- name: Build runtime image ${{ matrix.base_image.image }} for fork
if: github.event.pull_request.head.repo.fork
uses: useblacksmith/build-push-action@v1
with:
context: .
file: enterprise/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
OPENHANDS_VERSION=${{ env.OPENHANDS_DOCKER_TAG }}
platforms: linux/amd64
# Add build provenance
provenance: true
# Add build attestations for better security
sbom: true
load: true
tags: ghcr.io/${{ env.REPO_OWNER }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image.tag }}
context: containers/runtime
- name: Cache Poetry dependencies
uses: useblacksmith/cache@v5
with:
path: |
~/.cache/pypoetry
~/.virtualenvs
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
lookup-only: true
restore-keys: |
${{ runner.os }}-poetry-
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: '3.12'
- name: Install poetry via pipx
run: pipx install poetry
- name: Install Python dependencies using Poetry
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
poetry run pip install pytest-xdist
# Install to be able to retry on failures for flaky tests
poetry run pip install pytest-rerunfailures
image_name=ghcr.io/${{ env.REPO_OWNER }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image.tag }}
# Setting RUN_AS_OPENHANDS to false means use root.
# That should mean SANDBOX_USER_ID is ignored but some tests do not check for RUN_AS_OPENHANDS.
TEST_RUNTIME=docker \
SANDBOX_USER_ID=$(id -u) \
SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
TEST_IN_CI=true \
RUN_AS_OPENHANDS=false \
poetry run pytest -n 7 -raRs --reruns 2 --reruns-delay 5 -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --durations=10
env:
DEBUG: "1"
# Run unit tests with the Docker runtime Docker images as openhands user
test_runtime_oh:
name: RT Unit Tests (openhands)
runs-on: blacksmith-8vcpu-ubuntu-2204
needs: [ghcr_build_runtime, define-matrix]
strategy:
matrix:
base_image: ${{ fromJson(needs.define-matrix.outputs.base_image) }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Download runtime source for fork
if: github.event.pull_request.head.repo.fork
uses: actions/download-artifact@v4
with:
name: runtime-src-${{ matrix.base_image.tag }}
path: containers/runtime
- name: Lowercase Repository Owner
run: |
echo REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
# Forked repos can't push to GHCR, so we need to rebuild using cache
- name: Build runtime image ${{ matrix.base_image.image }} for fork
if: github.event.pull_request.head.repo.fork
uses: useblacksmith/build-push-action@v1
with:
load: true
tags: ghcr.io/${{ env.REPO_OWNER }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image.tag }}
context: containers/runtime
- name: Cache Poetry dependencies
uses: useblacksmith/cache@v5
with:
path: |
~/.cache/pypoetry
~/.virtualenvs
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
lookup-only: true
restore-keys: |
${{ runner.os }}-poetry-
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: '3.12'
- 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
- name: Run runtime tests
run: |
# We install pytest-xdist in order to run tests across CPUs
poetry run pip install pytest-xdist
# Install to be able to retry on failures for flaky tests
poetry run pip install pytest-rerunfailures
image_name=ghcr.io/${{ env.REPO_OWNER }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image.tag }}
TEST_RUNTIME=docker \
SANDBOX_USER_ID=$(id -u) \
SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
TEST_IN_CI=true \
RUN_AS_OPENHANDS=true \
poetry run pytest -n 7 -raRs --reruns 2 --reruns-delay 5 -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --durations=10
env:
DEBUG: "1"
# The two following jobs (named identically) are to check whether all the runtime tests have passed as the
# "All Runtime Tests Passed" is a required job for PRs to merge
# We can remove this once the config changes
# Due to this bug: https://github.com/actions/runner/issues/2566, we want to create a job that runs when the
# prerequisites have been cancelled or failed so merging is disallowed, otherwise Github considers "skipped" as "success"
runtime_tests_check_success:
name: All Runtime Tests Passed
if: ${{ !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
runs-on: blacksmith-4vcpu-ubuntu-2204
needs: [test_runtime_root, test_runtime_oh, verify_hash_equivalence_in_runtime_and_app]
steps:
- name: All tests passed
run: echo "All runtime tests have passed successfully!"
runtime_tests_check_fail:
name: All Runtime Tests Passed
if: ${{ cancelled() || contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
runs-on: blacksmith-4vcpu-ubuntu-2204
needs: [test_runtime_root, test_runtime_oh, verify_hash_equivalence_in_runtime_and_app]
steps:
- name: Some tests failed
run: |
echo "Some runtime tests failed or were cancelled"
exit 1
update_pr_description:
name: Update PR Description
if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]'
@@ -268,7 +425,30 @@ jobs:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
SHORT_SHA: ${{ steps.short_sha.outputs.SHORT_SHA }}
shell: bash
run: |
echo "Updating PR description with Docker and uvx commands"
bash ${GITHUB_WORKSPACE}/.github/scripts/update_pr_description.sh
echo "updating PR description"
DOCKER_RUN_COMMAND="docker run -it --rm \
-p 3000:3000 \
-v /var/run/docker.sock:/var/run/docker.sock \
--add-host host.docker.internal:host-gateway \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:$SHORT_SHA-nikolaik \
--name openhands-app-$SHORT_SHA \
docker.all-hands.dev/all-hands-ai/openhands:$SHORT_SHA"
PR_BODY=$(gh pr view $PR_NUMBER --json body --jq .body)
if echo "$PR_BODY" | grep -q "To run this PR locally, use the following command:"; then
UPDATED_PR_BODY=$(echo "${PR_BODY}" | sed -E "s|docker run -it --rm.*|$DOCKER_RUN_COMMAND|")
else
UPDATED_PR_BODY="${PR_BODY}
---
To run this PR locally, use the following command:
\`\`\`
$DOCKER_RUN_COMMAND
\`\`\`"
fi
echo "updated body: $UPDATED_PR_BODY"
gh pr edit $PR_NUMBER --body "$UPDATED_PR_BODY"

199
.github/workflows/integration-runner.yml vendored Normal file
View File

@@ -0,0 +1,199 @@
name: Run Integration Tests
on:
pull_request:
types: [labeled]
workflow_dispatch:
inputs:
reason:
description: 'Reason for manual trigger'
required: true
default: ''
schedule:
- cron: '30 22 * * *' # Runs at 10:30pm UTC every day
env:
N_PROCESSES: 10 # Global configuration for number of parallel processes for evaluation
jobs:
run-integration-tests:
if: github.event.label.name == 'integration-test' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
runs-on: blacksmith-4vcpu-ubuntu-2204
permissions:
contents: "read"
id-token: "write"
pull-requests: "write"
issues: "write"
strategy:
matrix:
python-version: ["3.12"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: "poetry"
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version: '22.x'
- name: Comment on PR if 'integration-test' label is present
if: github.event_name == 'pull_request' && github.event.label.name == 'integration-test'
uses: KeisukeYamashita/create-comment@v1
with:
unique: false
comment: |
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 --with dev,test,runtime
- name: Configure config.toml for testing with Haiku
env:
LLM_MODEL: "litellm_proxy/claude-3-5-haiku-20241022"
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
MAX_ITERATIONS: 10
run: |
echo "[llm.eval]" > config.toml
echo "model = \"$LLM_MODEL\"" >> config.toml
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
echo "temperature = 0.0" >> config.toml
- name: Build environment
run: make build
- name: Run integration test evaluation for Haiku
env:
SANDBOX_FORCE_REBUILD_RUNTIME: True
run: |
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD CodeActAgent '' 10 $N_PROCESSES '' 'haiku_run'
# get integration tests report
REPORT_FILE_HAIKU=$(find evaluation/evaluation_outputs/outputs/integration_tests/CodeActAgent/*haiku*_maxiter_10_N* -name "report.md" -type f | head -n 1)
echo "REPORT_FILE: $REPORT_FILE_HAIKU"
echo "INTEGRATION_TEST_REPORT_HAIKU<<EOF" >> $GITHUB_ENV
cat $REPORT_FILE_HAIKU >> $GITHUB_ENV
echo >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Wait a little bit
run: sleep 10
- name: Configure config.toml for testing with DeepSeek
env:
LLM_MODEL: "litellm_proxy/deepseek-chat"
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
MAX_ITERATIONS: 10
run: |
echo "[llm.eval]" > config.toml
echo "model = \"$LLM_MODEL\"" >> config.toml
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
echo "temperature = 0.0" >> config.toml
- name: Run integration test evaluation for DeepSeek
env:
SANDBOX_FORCE_REBUILD_RUNTIME: True
run: |
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD CodeActAgent '' 10 $N_PROCESSES '' 'deepseek_run'
# get integration tests report
REPORT_FILE_DEEPSEEK=$(find evaluation/evaluation_outputs/outputs/integration_tests/CodeActAgent/deepseek*_maxiter_10_N* -name "report.md" -type f | head -n 1)
echo "REPORT_FILE: $REPORT_FILE_DEEPSEEK"
echo "INTEGRATION_TEST_REPORT_DEEPSEEK<<EOF" >> $GITHUB_ENV
cat $REPORT_FILE_DEEPSEEK >> $GITHUB_ENV
echo >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
# -------------------------------------------------------------
# Run VisualBrowsingAgent tests for DeepSeek, limited to t05 and t06
- name: Wait a little bit (again)
run: sleep 5
- name: Configure config.toml for testing VisualBrowsingAgent (DeepSeek)
env:
LLM_MODEL: "litellm_proxy/deepseek-chat"
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
MAX_ITERATIONS: 15
run: |
echo "[llm.eval]" > config.toml
echo "model = \"$LLM_MODEL\"" >> config.toml
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
echo "temperature = 0.0" >> config.toml
- name: Run integration test evaluation for VisualBrowsingAgent (DeepSeek)
env:
SANDBOX_FORCE_REBUILD_RUNTIME: True
run: |
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD VisualBrowsingAgent '' 15 $N_PROCESSES "t05_simple_browsing,t06_github_pr_browsing.py" 'visualbrowsing_deepseek_run'
# Find and export the visual browsing agent test results
REPORT_FILE_VISUALBROWSING_DEEPSEEK=$(find evaluation/evaluation_outputs/outputs/integration_tests/VisualBrowsingAgent/deepseek*_maxiter_15_N* -name "report.md" -type f | head -n 1)
echo "REPORT_FILE_VISUALBROWSING_DEEPSEEK: $REPORT_FILE_VISUALBROWSING_DEEPSEEK"
echo "INTEGRATION_TEST_REPORT_VISUALBROWSING_DEEPSEEK<<EOF" >> $GITHUB_ENV
cat $REPORT_FILE_VISUALBROWSING_DEEPSEEK >> $GITHUB_ENV
echo >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Create archive of evaluation outputs
run: |
TIMESTAMP=$(date +'%y-%m-%d-%H-%M')
cd evaluation/evaluation_outputs/outputs # Change to the outputs directory
tar -czvf ../../../integration_tests_${TIMESTAMP}.tar.gz integration_tests/CodeActAgent/* integration_tests/VisualBrowsingAgent/* # Only include the actual result directories
- name: Upload evaluation results as artifact
uses: actions/upload-artifact@v4
id: upload_results_artifact
with:
name: integration-test-outputs-${{ github.run_id }}-${{ github.run_attempt }}
path: integration_tests_*.tar.gz
- name: Get artifact URLs
run: |
echo "ARTIFACT_URL=${{ steps.upload_results_artifact.outputs.artifact-url }}" >> $GITHUB_ENV
- name: Set timestamp and trigger reason
run: |
echo "TIMESTAMP=$(date +'%Y-%m-%d-%H-%M')" >> $GITHUB_ENV
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "TRIGGER_REASON=pr-${{ github.event.pull_request.number }}" >> $GITHUB_ENV
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "TRIGGER_REASON=manual-${{ github.event.inputs.reason }}" >> $GITHUB_ENV
else
echo "TRIGGER_REASON=nightly-scheduled" >> $GITHUB_ENV
fi
- name: Comment with results and artifact link
id: create_comment
uses: KeisukeYamashita/create-comment@v1
with:
# if triggered by PR, use PR number, otherwise use 5318 as fallback issue number for manual triggers
number: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || 5318 }}
unique: false
comment: |
Trigger by: ${{ github.event_name == 'pull_request' && format('Pull Request (integration-test label on PR #{0})', github.event.pull_request.number) || (github.event_name == 'workflow_dispatch' && format('Manual Trigger: {0}', github.event.inputs.reason)) || 'Nightly Scheduled Run' }}
Commit: ${{ github.sha }}
**Integration Tests Report (Haiku)**
Haiku LLM Test Results:
${{ env.INTEGRATION_TEST_REPORT_HAIKU }}
---
**Integration Tests Report (DeepSeek)**
DeepSeek LLM Test Results:
${{ env.INTEGRATION_TEST_REPORT_DEEPSEEK }}
---
**Integration Tests Report VisualBrowsing (DeepSeek)**
${{ env.INTEGRATION_TEST_REPORT_VISUALBROWSING_DEEPSEEK }}
---
Download testing outputs (includes both Haiku and DeepSeek results): [Download](${{ steps.upload_results_artifact.outputs.artifact-url }})

View File

@@ -21,20 +21,14 @@ jobs:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Node.js 22
- name: Install Node.js 20
uses: useblacksmith/setup-node@v5
with:
node-version: 22
node-version: 20
- name: Install frontend dependencies
run: |
cd frontend
npm install --frozen-lockfile
- name: Generate i18n and route types
run: |
cd frontend
npm run make-i18n
npx react-router typegen || true
- name: Fix frontend lint issues
run: |
cd frontend
@@ -51,7 +45,7 @@ jobs:
git config --local user.email "openhands@all-hands.dev"
git config --local user.name "OpenHands Bot"
git add -A
git commit -m "🤖 Auto-fix frontend linting issues" --no-verify
git commit -m "🤖 Auto-fix frontend linting issues"
git push
# Python lint fixes
@@ -74,13 +68,13 @@ jobs:
uses: useblacksmith/setup-python@v6
with:
python-version: 3.12
cache: "pip"
cache: 'pip'
- name: Install pre-commit
run: pip install pre-commit==3.7.0
- name: Fix python lint issues
run: |
# Run all pre-commit hooks and continue even if they modify files (exit code 1)
pre-commit run --config ./dev_config/python/.pre-commit-config.yaml --all-files || true
pre-commit run --config ./dev_config/python/.pre-commit-config.yaml --files openhands/**/* evaluation/**/* tests/**/* || true
# Commit and push changes if any
- name: Check for changes
@@ -93,5 +87,5 @@ jobs:
git config --local user.email "openhands@all-hands.dev"
git config --local user.name "OpenHands Bot"
git add -A
git commit -m "🤖 Auto-fix Python linting issues" --no-verify
git commit -m "🤖 Auto-fix Python linting issues"
git push

View File

@@ -7,7 +7,7 @@ name: Lint
on:
push:
branches:
- main
- main
pull_request:
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
@@ -22,10 +22,10 @@ jobs:
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
- name: Install Node.js 22
- name: Install Node.js 20
uses: useblacksmith/setup-node@v5
with:
node-version: 22
node-version: 20
- name: Install dependencies
run: |
cd frontend
@@ -49,26 +49,21 @@ jobs:
uses: useblacksmith/setup-python@v6
with:
python-version: 3.12
cache: "pip"
cache: 'pip'
- name: Install pre-commit
run: pip install pre-commit==3.7.0
- name: Run pre-commit hooks
run: pre-commit run --all-files --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
run: pre-commit run --files openhands/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
lint-enterprise-python:
name: Lint enterprise python
# Check version consistency across documentation
check-version-consistency:
name: Check version consistency
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up python
uses: useblacksmith/setup-python@v6
with:
python-version: 3.12
cache: "pip"
- name: Install pre-commit
run: pip install pre-commit==4.2.0
- name: Run pre-commit hooks
working-directory: ./enterprise
run: pre-commit run --all-files --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
- name: Run version consistency check
run: .github/scripts/check_version_consistency.py

View File

@@ -1,108 +0,0 @@
name: Publish OpenHands UI Package
# * Always run on "main"
# * Run on PRs that have changes in the "openhands-ui" folder or this workflow
on:
push:
branches:
- main
paths:
- "openhands-ui/**"
- ".github/workflows/npm-publish-ui.yml"
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
concurrency:
group: npm-publish-ui
cancel-in-progress: false
jobs:
check-version:
name: Check if version has changed
runs-on: blacksmith-4vcpu-ubuntu-2204
defaults:
run:
shell: bash
outputs:
should-publish: ${{ steps.version-check.outputs.should-publish }}
current-version: ${{ steps.version-check.outputs.current-version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2 # Need previous commit to compare
- name: Check if version changed
id: version-check
run: |
# Get current version from package.json
CURRENT_VERSION=$(jq -r .version openhands-ui/package.json)
echo "current-version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
# Check if package.json version changed in this commit
if git diff HEAD~1 HEAD --name-only | grep -q "openhands-ui/package.json"; then
# Check if the version field specifically changed
if git diff HEAD~1 HEAD openhands-ui/package.json | grep -q '"version"'; then
echo "Version changed in package.json, will publish"
echo "should-publish=true" >> $GITHUB_OUTPUT
else
echo "package.json changed but version did not change, skipping publish"
echo "should-publish=false" >> $GITHUB_OUTPUT
fi
else
echo "package.json did not change, skipping publish"
echo "should-publish=false" >> $GITHUB_OUTPUT
fi
publish:
name: Publish to npm
runs-on: blacksmith-4vcpu-ubuntu-2204
needs: check-version
if: needs.check-version.outputs.should-publish == 'true'
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: "openhands-ui/.bun-version"
- name: Install dependencies
working-directory: ./openhands-ui
run: bun install --frozen-lockfile
- name: Build package
working-directory: ./openhands-ui
run: bun run build
- name: Check if package already exists on npm
id: npm-check
working-directory: ./openhands-ui
run: |
PACKAGE_NAME=$(jq -r .name package.json)
VERSION="${{ needs.check-version.outputs.current-version }}"
# Check if this version already exists on npm
if npm view "$PACKAGE_NAME@$VERSION" version 2>/dev/null; then
echo "Version $VERSION already exists on npm, skipping publish"
echo "already-exists=true" >> $GITHUB_OUTPUT
else
echo "Version $VERSION does not exist on npm, proceeding with publish"
echo "already-exists=false" >> $GITHUB_OUTPUT
fi
- name: Setup npm authentication
if: steps.npm-check.outputs.already-exists == 'false'
run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
- name: Publish to npm
if: steps.npm-check.outputs.already-exists == 'false'
working-directory: ./openhands-ui
run: |
# The prepublishOnly script will run automatically and build the package
npm publish
echo "✅ Successfully published @openhands/ui@${{ needs.check-version.outputs.current-version }} to npm"

View File

@@ -89,7 +89,7 @@ jobs:
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Upgrade pip
@@ -118,7 +118,7 @@ jobs:
contains(github.event.review.body, '@openhands-agent-exp')
)
)
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: ${{ env.pythonLocation }}/lib/python3.12/site-packages/*
key: ${{ runner.os }}-pip-openhands-resolver-${{ hashFiles('/tmp/requirements.txt') }}
@@ -201,7 +201,7 @@ jobs:
issue_number: ${{ env.ISSUE_NUMBER }},
owner: context.repo.owner,
repo: context.repo.repo,
body: `[OpenHands](https://github.com/OpenHands/OpenHands) started fixing the ${issueType}! You can monitor the progress [here](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).`
body: `[OpenHands](https://github.com/All-Hands-AI/OpenHands) started fixing the ${issueType}! You can monitor the progress [here](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).`
});
- name: Install OpenHands
@@ -233,7 +233,7 @@ jobs:
if (isExperimentalLabel || isIssueCommentExperimental || isReviewCommentExperimental) {
console.log("Installing experimental OpenHands...");
await exec.exec("pip install git+https://github.com/openhands/openhands.git");
await exec.exec("pip install git+https://github.com/all-hands-ai/openhands.git");
} else {
console.log("Installing from requirements.txt...");
@@ -269,7 +269,7 @@ jobs:
fi
- name: Upload output.jsonl as artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
if: always() # Upload even if the previous steps fail
with:
name: resolver-output

View File

@@ -1,48 +0,0 @@
---
name: PR Review by OpenHands
on:
# TEMPORARY MITIGATION (Clinejection hardening)
#
# We temporarily avoid `pull_request_target` here. We'll restore it after the PR review
# workflow is fully hardened for untrusted execution.
pull_request:
types: [opened, ready_for_review, labeled, review_requested]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
pr-review:
# Note: fork PRs will not have access to repository secrets under `pull_request`.
# Skip forks to avoid noisy failures until we restore a hardened `pull_request_target` flow.
if: |
github.event.pull_request.head.repo.full_name == github.repository &&
(
(github.event.action == 'opened' && github.event.pull_request.draft == false) ||
github.event.action == 'ready_for_review' ||
(github.event.action == 'labeled' && github.event.label.name == 'review-this') ||
(
github.event.action == 'review_requested' &&
(
github.event.requested_reviewer.login == 'openhands-agent' ||
github.event.requested_reviewer.login == 'all-hands-bot'
)
)
)
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
runs-on: ubuntu-24.04
steps:
- name: Run PR Review
uses: OpenHands/extensions/plugins/pr-review@main
with:
llm-model: litellm_proxy/claude-sonnet-4-5-20250929
llm-base-url: https://llm-proxy.app.all-hands.dev
review-style: roasted
llm-api-key: ${{ secrets.LLM_API_KEY }}
github-token: ${{ secrets.ALLHANDS_BOT_GITHUB_PAT }}
lmnr-api-key: ${{ secrets.LMNR_SKILLS_API_KEY }}

View File

@@ -1,85 +0,0 @@
---
name: PR Review Evaluation
# This workflow evaluates how well PR review comments were addressed.
# It runs when a PR is closed to assess review effectiveness.
#
# Security note: pull_request_target is safe here because:
# 1. Only triggers on PR close (not on code changes)
# 2. Does not checkout PR code - only downloads artifacts from trusted workflow runs
# 3. Runs evaluation scripts from the extensions repo, not from the PR
on:
pull_request_target:
types: [closed]
permissions:
contents: read
pull-requests: read
jobs:
evaluate:
runs-on: ubuntu-24.04
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_NAME: ${{ github.repository }}
PR_MERGED: ${{ github.event.pull_request.merged }}
steps:
- name: Download review trace artifact
id: download-trace
uses: dawidd6/action-download-artifact@v6
continue-on-error: true
with:
workflow: pr-review-by-openhands.yml
name: pr-review-trace-${{ github.event.pull_request.number }}
path: trace-info
search_artifacts: true
if_no_artifact_found: warn
- name: Check if trace file exists
id: check-trace
run: |
if [ -f "trace-info/laminar_trace_info.json" ]; then
echo "trace_exists=true" >> $GITHUB_OUTPUT
echo "Found trace file for PR #$PR_NUMBER"
else
echo "trace_exists=false" >> $GITHUB_OUTPUT
echo "No trace file found for PR #$PR_NUMBER - skipping evaluation"
fi
# Always checkout main branch for security - cannot test script changes in PRs
- name: Checkout extensions repository
if: steps.check-trace.outputs.trace_exists == 'true'
uses: actions/checkout@v5
with:
repository: OpenHands/extensions
path: extensions
- name: Set up Python
if: steps.check-trace.outputs.trace_exists == 'true'
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install dependencies
if: steps.check-trace.outputs.trace_exists == 'true'
run: pip install lmnr
- name: Run evaluation
if: steps.check-trace.outputs.trace_exists == 'true'
env:
# Script expects LMNR_PROJECT_API_KEY; org secret is named LMNR_SKILLS_API_KEY
LMNR_PROJECT_API_KEY: ${{ secrets.LMNR_SKILLS_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python extensions/plugins/pr-review/scripts/evaluate_review.py \
--trace-file trace-info/laminar_trace_info.json
- name: Upload evaluation logs
uses: actions/upload-artifact@v5
if: always() && steps.check-trace.outputs.trace_exists == 'true'
with:
name: pr-review-evaluation-${{ github.event.pull_request.number }}
path: '*.log'
retention-days: 30

View File

@@ -1,127 +0,0 @@
# 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"
# * Always run on PRs
on:
push:
branches:
- main
pull_request:
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
concurrency:
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
cancel-in-progress: true
jobs:
# Run python tests on Linux
test-on-linux:
name: Python Tests on Linux
runs-on: blacksmith-4vcpu-ubuntu-2404
env:
INSTALL_DOCKER: "0" # Set to '0' to skip Docker installation
strategy:
matrix:
python-version: ["3.12"]
permissions:
# For coverage comment and python-coverage-comment-action branch
pull-requests: write
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Install tmux
run: sudo apt-get update && sudo apt-get install -y tmux
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version: "22.x"
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: "poetry"
- name: Install Python dependencies using Poetry
run: |
poetry install --with dev,test,runtime
poetry run pip install pytest-xdist
poetry run pip install pytest-rerunfailures
- name: Build Environment
run: make build
- name: Run Unit Tests
run: PYTHONPATH=".:$PYTHONPATH" poetry run pytest --forked -n auto -s ./tests/unit --cov=openhands --cov-branch
env:
COVERAGE_FILE: ".coverage.${{ matrix.python_version }}"
- name: Run Runtime Tests with CLIRuntime
run: PYTHONPATH=".:$PYTHONPATH" TEST_RUNTIME=cli poetry run pytest -n 5 --reruns 2 --reruns-delay 3 -s tests/runtime/test_bash.py --cov=openhands --cov-branch
env:
COVERAGE_FILE: ".coverage.runtime.${{ matrix.python_version }}"
- name: Store coverage file
uses: actions/upload-artifact@v6
with:
name: coverage-openhands
path: |
.coverage.${{ matrix.python_version }}
.coverage.runtime.${{ matrix.python_version }}
include-hidden-files: true
test-enterprise:
name: Enterprise Python Unit Tests
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: "poetry"
- name: Install Python dependencies using Poetry
working-directory: ./enterprise
run: poetry install --with dev,test
- name: Run Unit Tests
# Use base working directory for coverage paths to line up.
run: PYTHONPATH=".:$PYTHONPATH" poetry run --project=enterprise pytest --forked -n auto -s -p no:ddtrace -p no:ddtrace.pytest_bdd -p no:ddtrace.pytest_benchmark ./enterprise/tests/unit --cov=enterprise --cov-branch
env:
COVERAGE_FILE: ".coverage.enterprise.${{ matrix.python_version }}"
- name: Store coverage file
uses: actions/upload-artifact@v6
with:
name: coverage-enterprise
path: ".coverage.enterprise.${{ matrix.python_version }}"
include-hidden-files: true
coverage-comment:
name: Coverage Comment
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
needs: [test-on-linux, test-enterprise]
permissions:
pull-requests: write
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v6
id: download
with:
pattern: coverage-*
merge-multiple: true
- name: Coverage comment
id: coverage_comment
uses: py-cov-action/python-coverage-comment-action@v3
with:
GITHUB_TOKEN: ${{ github.token }}
MERGE_COVERAGE_FILES: true

84
.github/workflows/py-unit-tests.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
# Workflow that runs python unit tests
name: Run Python Unit Tests
# The jobs in this workflow are required, so they must run at all times
# * Always run on "main"
# * Always run on PRs
on:
push:
branches:
- main
pull_request:
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
concurrency:
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
cancel-in-progress: true
jobs:
# Run python unit tests on Linux
test-on-linux:
name: Python Unit Tests on Linux
runs-on: blacksmith-4vcpu-ubuntu-2204
env:
INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation
strategy:
matrix:
python-version: ['3.12']
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Install tmux
run: sudo apt-get update && sudo apt-get install -y tmux
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version: '22.x'
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: useblacksmith/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install --with dev,test,runtime
- name: Build Environment
run: make build
- name: Run Unit Tests
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
# Run specific Windows python tests
test-on-windows:
name: Python Tests on Windows
runs-on: windows-latest
strategy:
matrix:
python-version: ['3.12']
steps:
- uses: actions/checkout@v4
- name: Install pipx
run: pip install pipx
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install --with dev,test,runtime
- name: Run Windows unit tests
run: poetry run pytest -svv tests/unit/test_windows_bash.py
env:
DEBUG: "1"
- name: Run Windows runtime tests with LocalRuntime
run: $env:TEST_RUNTIME="local"; poetry run pytest -svv tests/runtime/test_bash.py
env:
TEST_RUNTIME: local
DEBUG: "1"

View File

@@ -1,27 +1,18 @@
# Publishes the OpenHands PyPi package
name: Publish PyPi Package
# Triggered manually
on:
workflow_dispatch:
inputs:
reason:
description: "What are you publishing?"
description: 'Reason for manual trigger'
required: true
type: choice
options:
- app server
default: app server
push:
tags:
- "*"
default: ''
jobs:
release:
runs-on: blacksmith-4vcpu-ubuntu-2204
# Run when manually dispatched for "app server" OR for tag pushes that don't contain '-cli'
if: |
(github.event_name == 'workflow_dispatch' && github.event.inputs.reason == 'app server')
|| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-cli'))
steps:
- uses: actions/checkout@v4
- uses: useblacksmith/setup-python@v6

56
.github/workflows/run-eval.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
# Run evaluation on a PR
name: Run Eval
# Runs when a PR is labeled with one of the "run-eval-" labels
on:
pull_request:
types: [labeled]
jobs:
trigger-job:
name: Trigger remote eval job
if: ${{ github.event.label.name == 'run-eval-1' || github.event.label.name == 'run-eval-2' || github.event.label.name == 'run-eval-50' || github.event.label.name == 'run-eval-100' }}
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Trigger remote job
env:
PR_BRANCH: ${{ github.head_ref }}
run: |
REPO_URL="https://github.com/${{ github.repository }}"
echo "Repository URL: $REPO_URL"
echo "PR Branch: $PR_BRANCH"
if [[ "${{ github.event.label.name }}" == "run-eval-1" ]]; then
EVAL_INSTANCES="1"
elif [[ "${{ github.event.label.name }}" == "run-eval-2" ]]; then
EVAL_INSTANCES="2"
elif [[ "${{ github.event.label.name }}" == "run-eval-50" ]]; then
EVAL_INSTANCES="50"
elif [[ "${{ github.event.label.name }}" == "run-eval-100" ]]; then
EVAL_INSTANCES="100"
fi
curl -X POST \
-H "Authorization: Bearer ${{ secrets.PAT_TOKEN }}" \
-H "Accept: application/vnd.github+json" \
-d "{\"ref\": \"main\", \"inputs\": {\"github-repo\": \"${REPO_URL}\", \"github-branch\": \"${PR_BRANCH}\", \"pr-number\": \"${{ github.event.pull_request.number }}\", \"eval-instances\": \"${EVAL_INSTANCES}\"}}" \
https://api.github.com/repos/All-Hands-AI/evaluation/actions/workflows/create-branch.yml/dispatches
# Send Slack message
PR_URL="https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
slack_text="PR $PR_URL has triggered evaluation on $EVAL_INSTANCES instances..."
curl -X POST -H 'Content-type: application/json' --data '{"text":"'"$slack_text"'"}' \
https://hooks.slack.com/services/${{ secrets.SLACK_TOKEN }}
- name: Comment on PR
uses: KeisukeYamashita/create-comment@v1
with:
unique: false
comment: |
Running evaluation on the PR. Once eval is done, the results will be posted.

View File

@@ -9,15 +9,14 @@ on:
jobs:
stale:
runs-on: blacksmith-4vcpu-ubuntu-2204
if: github.repository == 'OpenHands/OpenHands'
steps:
- uses: actions/stale@v9
with:
stale-issue-message: 'This issue is stale because it has been open for 40 days with no activity. Remove the stale label or leave a comment, otherwise it will be closed in 10 days.'
stale-pr-message: 'This PR is stale because it has been open for 40 days with no activity. Remove the stale label or leave a comment, otherwise it will be closed in 10 days.'
days-before-stale: 40
exempt-issue-labels: roadmap,backlog,app-team
close-issue-message: 'This issue was automatically closed due to 50 days of inactivity. We do this to help keep the issues somewhat manageable and focus on active issues.'
close-pr-message: 'This PR was closed because it had no activity for 50 days. If you feel this was closed in error, and you would like to continue the PR, please resubmit or let us know.'
days-before-close: 10
operations-per-run: 300
stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
days-before-stale: 30
exempt-issue-labels: 'tracked'
close-issue-message: 'This issue was closed because it has been stalled for over 30 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for over 30 days with no activity.'
days-before-close: 7
operations-per-run: 150

View File

@@ -1,34 +0,0 @@
name: Run UI Component Build
# * Always run on "main"
# * Run on PRs that have changes in the "openhands-ui" folder or this workflow
on:
push:
branches:
- main
pull_request:
paths:
- 'openhands-ui/**'
- '.github/workflows/ui-build.yml'
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
concurrency:
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
cancel-in-progress: true
jobs:
ui-build:
name: Build openhands-ui
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: "openhands-ui/.bun-version"
- name: Install dependencies
working-directory: ./openhands-ui
run: bun install --frozen-lockfile
- name: Build package
working-directory: ./openhands-ui
run: bun run build

View File

@@ -1,51 +0,0 @@
name: Welcome Good First Issue
on:
issues:
types: [labeled]
permissions:
issues: write
jobs:
comment-on-good-first-issue:
if: github.event.label.name == 'good first issue'
runs-on: ubuntu-latest
steps:
- name: Check if welcome comment already exists
id: check_comment
uses: actions/github-script@v7
with:
result-encoding: string
script: |
const issueNumber = context.issue.number;
const comments = await github.rest.issues.listComments({
...context.repo,
issue_number: issueNumber
});
const alreadyCommented = comments.data.some(
(comment) =>
comment.body.includes('<!-- auto-comment:good-first-issue -->')
);
return alreadyCommented ? 'true' : 'false';
- name: Leave welcome comment
if: steps.check_comment.outputs.result == 'false'
uses: actions/github-script@v7
with:
script: |
const repoUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}`;
await github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: "🙌 **Hey there, future contributor!** 🙌\n\n" +
"This issue has been labeled as **good first issue**, which means it's a great place to get started with the OpenHands project.\n\n" +
"If you're interested in working on it, feel free to! No need to ask for permission.\n\n" +
"Be sure to check out our [development setup guide](" + repoUrl + "/blob/main/Development.md) to get your environment set up, and follow our [contribution guidelines](" + repoUrl + "/blob/main/CONTRIBUTING.md) when you're ready to submit a fix.\n\n" +
"Feel free to join our developer community on [Slack](https://openhands.dev/joinslack). You can ask for [help](https://openhands-ai.slack.com/archives/C078L0FUGUX), [feedback](https://openhands-ai.slack.com/archives/C086ARSNMGA), and even ask for a [PR review](https://openhands-ai.slack.com/archives/C08D8FJ5771).\n\n" +
"🙌 Happy hacking! 🙌\n\n" +
"<!-- auto-comment:good-first-issue -->"
});

10
.gitignore vendored
View File

@@ -182,11 +182,6 @@ cython_debug/
.roo/rules
.cline/rules
.windsurf/rules
.repomix
repomix-output.txt
# Emacs backup
*~
# evaluation
evaluation/evaluation_outputs
@@ -257,8 +252,3 @@ containers/runtime/Dockerfile
containers/runtime/project.tar.gz
containers/runtime/code
**/node_modules/
# test results
test-results
.sessions
.eval_sessions

View File

@@ -121,7 +121,7 @@ A specialized prompt that enhances OpenHands with domain-specific knowledge, rep
A central repository of available microagents and their configurations.
#### Public Microagent
A general-purpose microagent available to all OpenHands users, triggered by specific keywords. Located in `microagents/`.
A general-purpose microagent available to all OpenHands users, triggered by specific keywords.
#### Repository Microagent
A type of microagent that provides repository-specific context and guidelines, stored in the `.openhands/microagents/` directory.

View File

@@ -0,0 +1,83 @@
This repository contains the code for OpenHands, an automated AI software engineer. It has a Python backend
(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.
Before pushing any changes, you MUST ensure that any lint errors or simple test errors have been fixed.
* If you've made changes to the backend, you should run `pre-commit run --config ./dev_config/python/.pre-commit-config.yaml` (this will run on staged files).
* If you've made changes to the frontend, you should run `cd frontend && npm run lint:fix && npm run build ; cd ..`
The pre-commit hooks MUST pass successfully before pushing any changes to the repository. This is a mandatory requirement to maintain code quality and consistency.
If either command fails, it may have automatically fixed some issues. You should fix any issues that weren't automatically fixed,
then re-run the command to ensure it passes. Common issues include:
- Mypy type errors
- Ruff formatting issues
- Trailing whitespace
- Missing newlines at end of files
## 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
- Prerequisites: A recent version of NodeJS / NPM
- Setup: Run `npm install` in the frontend directory
- Testing:
- Run tests: `npm run test`
- To run specific tests: `npm run test -- -t "TestName"`
- Our test framework is vitest
- Building:
- Build for production: `npm run build`
- Environment Variables:
- Set in `frontend/.env` or as environment variables
- Available variables: VITE_BACKEND_HOST, VITE_USE_TLS, VITE_INSECURE_SKIP_VERIFY, VITE_FRONTEND_PORT
- Internationalization:
- Generate i18n declaration file: `npm run make-i18n`
## Template for Github Pull Request
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
## Implementation Details
These details may or may not be useful for your current task.
### Frontend
#### Action Handling:
- Actions are defined in `frontend/src/types/action-type.ts`
- The `HANDLED_ACTIONS` array in `frontend/src/state/chat-slice.ts` determines which actions are displayed as collapsible UI elements
- To add a new action type to the UI:
1. Add the action type to the `HANDLED_ACTIONS` array
2. Implement the action handling in `addAssistantAction` function in chat-slice.ts
3. Add a translation key in the format `ACTION_MESSAGE$ACTION_NAME` to the i18n files
- Actions with `thought` property are displayed in the UI based on their action type:
- Regular actions (like "run", "edit") display the thought as a separate message
- Special actions (like "think") are displayed as collapsible elements only
#### Adding User Settings:
- To add a new user setting to OpenHands, follow these steps:
1. Add the setting to the frontend:
- Add the setting to the `Settings` type in `frontend/src/types/settings.ts`
- Add the setting to the `ApiSettings` type in the same file
- Add the setting with an appropriate default value to `DEFAULT_SETTINGS` in `frontend/src/services/settings.ts`
- Update the `useSettings` hook in `frontend/src/hooks/query/use-settings.ts` to map the API response
- Update the `useSaveSettings` hook in `frontend/src/hooks/mutation/use-save-settings.ts` to include the setting in API requests
- Add UI components (like toggle switches) in the appropriate settings screen (e.g., `frontend/src/routes/app-settings.tsx`)
- Add i18n translations for the setting name and any tooltips in `frontend/src/i18n/translation.json`
- 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)

View File

@@ -1,107 +1,56 @@
#!/bin/bash
echo "Running OpenHands pre-commit hook..."
echo "This hook runs selective linting based on changed files."
# Store the exit code to return at the end
# This allows us to be additive to existing pre-commit hooks
EXIT_CODE=0
# Get the list of staged files
STAGED_FILES=$(git diff --cached --name-only)
# Check if frontend directory has changed
frontend_changes=$(git diff --cached --name-only | grep "^frontend/")
if [ -n "$frontend_changes" ]; then
echo "Frontend changes detected. Running frontend checks..."
# Check if any files match specific patterns
has_frontend_changes=false
has_backend_changes=false
# Check if frontend directory exists
if [ -d "frontend" ]; then
# Change to frontend directory
cd frontend || exit 1
# Check each file individually to avoid issues with grep
for file in $STAGED_FILES; do
if [[ $file == frontend/* ]]; then
has_frontend_changes=true
elif [[ $file == openhands/* || $file == evaluation/* || $file == tests/* ]]; then
has_backend_changes=true
fi
done
echo "Analyzing changes..."
echo "- Frontend changes: $has_frontend_changes"
echo "- Backend changes: $has_backend_changes"
# Run frontend linting if needed
if [ "$has_frontend_changes" = true ]; then
# Check if we're in a CI environment or if frontend dependencies are missing
if [ -n "$CI" ] || ! command -v react-router &> /dev/null || ! command -v vitest &> /dev/null; then
echo "Skipping frontend checks (CI environment or missing dependencies detected)."
echo "WARNING: Frontend files have changed but frontend checks are being skipped."
echo "Please run 'make lint-frontend' manually before submitting your PR."
else
echo "Running frontend linting..."
make lint-frontend
# Run lint:fix
echo "Running npm lint:fix..."
npm run lint:fix
if [ $? -ne 0 ]; then
echo "Frontend linting failed. Please fix the issues before committing."
EXIT_CODE=1
else
echo "Frontend linting checks passed!"
fi
# Run additional frontend checks
if [ -d "frontend" ]; then
echo "Running additional frontend checks..."
cd frontend || exit 1
# Run build
echo "Running npm build..."
npm run build
if [ $? -ne 0 ]; then
echo "Frontend build failed. Please fix the issues before committing."
EXIT_CODE=1
fi
# Run tests
echo "Running npm test..."
npm test
if [ $? -ne 0 ]; then
echo "Frontend tests failed. Please fix the failing tests before committing."
EXIT_CODE=1
fi
cd ..
fi
fi
else
echo "Skipping frontend checks (no frontend changes detected)."
fi
# Run backend linting if needed
if [ "$has_backend_changes" = true ]; then
echo "Running backend linting..."
make lint-backend
if [ $? -ne 0 ]; then
echo "Backend linting failed. Please fix the issues before committing."
EXIT_CODE=1
else
echo "Backend linting checks passed!"
fi
else
echo "Skipping backend checks (no backend changes detected)."
fi
# If no specific code changes detected, run basic checks
if [ "$has_frontend_changes" = false ] && [ "$has_backend_changes" = false ]; then
echo "No specific code changes detected. Running basic checks..."
if [ -n "$STAGED_FILES" ]; then
# Run only basic pre-commit hooks for non-code files
poetry run pre-commit run --files $(echo "$STAGED_FILES" | tr '\n' ' ') --hook-stage commit --config ./dev_config/python/.pre-commit-config.yaml
# Run build
echo "Running npm build..."
npm run build
if [ $? -ne 0 ]; then
echo "Basic checks failed. Please fix the issues before committing."
echo "Frontend build failed. Please fix the issues before committing."
EXIT_CODE=1
else
echo "Basic checks passed!"
fi
# Run tests
echo "Running npm test..."
npm test
if [ $? -ne 0 ]; then
echo "Frontend tests failed. Please fix the failing tests before committing."
EXIT_CODE=1
fi
# Return to the original directory
cd ..
if [ $EXIT_CODE -eq 0 ]; then
echo "Frontend checks passed!"
fi
else
echo "No files changed. Skipping basic checks."
echo "Frontend directory not found. Skipping frontend checks."
fi
else
echo "No frontend changes detected. Skipping frontend checks."
fi
# Run any existing pre-commit hooks that might have been installed by the user

16
.vscode/settings.json vendored
View File

@@ -3,20 +3,4 @@
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"python.defaultInterpreterPath": "./.venv/bin/python",
"python.terminal.activateEnvironment": true,
"python.analysis.autoImportCompletions": true,
"python.analysis.autoSearchPaths": true,
"python.analysis.extraPaths": [
"./.venv/lib/python3.12/site-packages"
],
"python.analysis.packageIndexDepths": [
{
"name": "openhands",
"depth": 10,
"includeAllSymbols": true
}
],
"python.analysis.stubPath": "./.venv/lib/python3.12/site-packages",
}

344
AGENTS.md
View File

@@ -1,344 +0,0 @@
This repository contains the code for OpenHands, an automated AI software engineer. It has a Python backend
(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.
## Running OpenHands with OpenHands:
To run the full application to debug issues:
```bash
export INSTALL_DOCKER=0
export RUNTIME=local
make build && make run FRONTEND_PORT=12000 FRONTEND_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0 &> /tmp/openhands-log.txt &
```
IMPORTANT: Before making any changes to the codebase, ALWAYS run `make install-pre-commit-hooks` to ensure pre-commit hooks are properly installed.
Before pushing any changes, you MUST ensure that any lint errors or simple test errors have been fixed.
* If you've made changes to the backend, you should run `pre-commit run --config ./dev_config/python/.pre-commit-config.yaml` (this will run on staged files).
* If you've made changes to the frontend, you should run `cd frontend && npm run lint:fix && npm run build ; cd ..`
* If you've made changes to the VSCode extension, you should run `cd openhands/integrations/vscode && npm run lint:fix && npm run compile ; cd ../../..`
The pre-commit hooks MUST pass successfully before pushing any changes to the repository. This is a mandatory requirement to maintain code quality and consistency.
If either command fails, it may have automatically fixed some issues. You should fix any issues that weren't automatically fixed,
then re-run the command to ensure it passes. Common issues include:
- Mypy type errors
- Ruff formatting issues
- Trailing whitespace
- Missing newlines at end of files
## Git Best Practices
- Prefer specific `git add <filename>` instead of `git add .` to avoid accidentally staging unintended files
- Be especially careful with `git reset --hard` after staging files, as it will remove accidentally staged files
- When remote has new changes, use `git fetch upstream && git rebase upstream/<branch>` on the same branch
## 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
- Prerequisites: A recent version of NodeJS / NPM
- Setup: Run `npm install` in the frontend directory
- Testing:
- Run tests: `npm run test`
- To run specific tests: `npm run test -- -t "TestName"`
- Our test framework is vitest
- Building:
- Build for production: `npm run build`
- Environment Variables:
- Set in `frontend/.env` or as environment variables
- Available variables: VITE_BACKEND_HOST, VITE_USE_TLS, VITE_INSECURE_SKIP_VERIFY, VITE_FRONTEND_PORT
- Internationalization:
- Generate i18n declaration file: `npm run make-i18n`
- Data Fetching & Cache Management:
- We use TanStack Query (fka React Query) for data fetching and cache management
- Data Access Layer: API client methods are located in `frontend/src/api` and should never be called directly from UI components - they must always be wrapped with TanStack Query
- Custom hooks are located in `frontend/src/hooks/query/` and `frontend/src/hooks/mutation/`
- Query hooks should follow the pattern use[Resource] (e.g., `useConversationSkills`)
- Mutation hooks should follow the pattern use[Action] (e.g., `useDeleteConversation`)
- Architecture rule: UI components → TanStack Query hooks → Data Access Layer (`frontend/src/api`) → API endpoints
VSCode Extension:
- Located in the `openhands/integrations/vscode` directory
- Setup: Run `npm install` in the extension directory
- Linting:
- Run linting with fixes: `npm run lint:fix`
- Check only: `npm run lint`
- Type checking: `npm run typecheck`
- Building:
- Compile TypeScript: `npm run compile`
- Package extension: `npm run package-vsix`
- Testing:
- Run tests: `npm run test`
- Development Best Practices:
- Use `vscode.window.createOutputChannel()` for debug logging instead of `showErrorMessage()` popups
- Pre-commit process runs both frontend and backend checks when committing extension changes
## Enterprise Directory
The `enterprise/` directory contains additional functionality that extends the open-source OpenHands codebase. This includes:
- Authentication and user management (Keycloak integration)
- Database migrations (Alembic)
- Integration services (GitHub, GitLab, Jira, Linear, Slack)
- Billing and subscription management (Stripe)
- Telemetry and analytics (PostHog, custom metrics framework)
### Enterprise Development Setup
**Prerequisites:**
- Python 3.12
- Poetry (for dependency management)
- Node.js 22.x (for frontend)
- Docker (optional)
**Setup Steps:**
1. First, build the main OpenHands project: `make build`
2. Then install enterprise dependencies: `cd enterprise && poetry install --with dev,test` (This can take a very long time. Be patient.)
3. Set up enterprise pre-commit hooks: `poetry run pre-commit install --config ./dev_config/python/.pre-commit-config.yaml`
**Running Enterprise Tests:**
```bash
# Enterprise unit tests (full suite)
PYTHONPATH=".:$PYTHONPATH" poetry run --project=enterprise pytest --forked -n auto -s -p no:ddtrace -p no:ddtrace.pytest_bdd -p no:ddtrace.pytest_benchmark ./enterprise/tests/unit --cov=enterprise --cov-branch
# Test specific modules (faster for development)
cd enterprise
PYTHONPATH=".:$PYTHONPATH" poetry run pytest tests/unit/telemetry/ --confcutdir=tests/unit/telemetry
# Enterprise linting (IMPORTANT: use --show-diff-on-failure to match GitHub CI)
poetry run pre-commit run --all-files --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
```
**Running Enterprise Server:**
```bash
cd enterprise
make start-backend # Development mode with hot reload
# or
make run # Full application (backend + frontend)
```
**Key Configuration Files:**
- `enterprise/pyproject.toml` - Enterprise-specific dependencies
- `enterprise/Makefile` - Enterprise build and run commands
- `enterprise/dev_config/python/` - Linting and type checking configuration
- `enterprise/migrations/` - Database migration files
**Database Migrations:**
Enterprise uses Alembic for database migrations. When making schema changes:
1. Create migration files in `enterprise/migrations/versions/`
2. Test migrations thoroughly
3. The CI will check for migration conflicts on PRs
**Integration Development:**
The enterprise codebase includes integrations for:
- **GitHub** - PR management, webhooks, app installations
- **GitLab** - Similar to GitHub but for GitLab instances
- **Jira** - Issue tracking and project management
- **Linear** - Modern issue tracking
- **Slack** - Team communication and notifications
Each integration follows a consistent pattern with service classes, storage models, and API endpoints.
**Important Notes:**
- Enterprise code is licensed under Polyform Free Trial License (30-day limit)
- The enterprise server extends the OpenHands server through dynamic imports
- Database changes require careful migration planning in `enterprise/migrations/`
- Always test changes in both OpenHands and enterprise contexts
- Use the enterprise-specific Makefile commands for development
**Enterprise Testing Best Practices:**
**Database Testing:**
- Use SQLite in-memory databases (`sqlite:///:memory:`) for unit tests instead of real PostgreSQL
- Create module-specific `conftest.py` files with database fixtures
- Mock external database connections in unit tests to avoid dependency on running services
- Use real database connections only for integration tests
**Import Patterns:**
- Use relative imports without `enterprise.` prefix in enterprise code
- Example: `from storage.database import a_session_maker` not `from enterprise.storage.database import a_session_maker`
- This ensures code works in both OpenHands and enterprise contexts
**Test Structure:**
- Place tests in `enterprise/tests/unit/` following the same structure as the source code
- Use `--confcutdir=tests/unit/[module]` when testing specific modules
- Create comprehensive fixtures for complex objects (databases, external services)
- Write platform-agnostic tests (avoid hardcoded OS-specific assertions)
**Mocking Strategy:**
- Use `AsyncMock` for async operations and `MagicMock` for complex objects
- Mock all external dependencies (databases, APIs, file systems) in unit tests
- Use `patch` with correct import paths (e.g., `telemetry.registry.logger` not `enterprise.telemetry.registry.logger`)
- Test both success and failure scenarios with proper error handling
**Coverage Goals:**
- Aim for 90%+ test coverage on new enterprise modules
- Focus on critical business logic and error handling paths
- Use `--cov-report=term-missing` to identify uncovered lines
**Troubleshooting:**
- If tests fail, ensure all dependencies are installed: `poetry install --with dev,test`
- For database issues, check migration status and run migrations if needed
- For frontend issues, ensure the main OpenHands frontend is built: `make build`
- Check logs in the `logs/` directory for runtime issues
- If tests fail with import errors, verify `PYTHONPATH=".:$PYTHONPATH"` is set
- **If GitHub CI fails but local linting passes**: Always use `--show-diff-on-failure` flag to match CI behavior exactly
## Template for Github Pull Request
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
## Implementation Details
These details may or may not be useful for your current task.
### Microagents
Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge and task-specific workflows. They are Markdown files that can include frontmatter for configuration.
#### Types:
- **Public Microagents**: Located in `microagents/`, available to all users
- **Repository Microagents**: Located in `.openhands/microagents/`, specific to this repository
#### Loading Behavior:
- **Without frontmatter**: Always loaded into LLM context
- **With triggers in frontmatter**: Only loaded when user's message matches the specified trigger keywords
#### Structure:
```yaml
---
triggers:
- keyword1
- keyword2
---
# Microagent Content
Your specialized knowledge and instructions here...
```
### Frontend
#### Action Handling:
- Actions are defined in `frontend/src/types/action-type.ts`
- The `HANDLED_ACTIONS` array in `frontend/src/state/chat-slice.ts` determines which actions are displayed as collapsible UI elements
- To add a new action type to the UI:
1. Add the action type to the `HANDLED_ACTIONS` array
2. Implement the action handling in `addAssistantAction` function in chat-slice.ts
3. Add a translation key in the format `ACTION_MESSAGE$ACTION_NAME` to the i18n files
- Actions with `thought` property are displayed in the UI based on their action type:
- Regular actions (like "run", "edit") display the thought as a separate message
- Special actions (like "think") are displayed as collapsible elements only
#### Adding User Settings:
- To add a new user setting to OpenHands, follow these steps:
1. Add the setting to the frontend:
- Add the setting to the `Settings` type in `frontend/src/types/settings.ts`
- Add the setting to the `ApiSettings` type in the same file
- Add the setting with an appropriate default value to `DEFAULT_SETTINGS` in `frontend/src/services/settings.ts`
- Update the `useSettings` hook in `frontend/src/hooks/query/use-settings.ts` to map the API response
- Update the `useSaveSettings` hook in `frontend/src/hooks/mutation/use-save-settings.ts` to include the setting in API requests
- Add UI components (like toggle switches) in the appropriate settings screen (e.g., `frontend/src/routes/app-settings.tsx`)
- Add i18n translations for the setting name and any tooltips in `frontend/src/i18n/translation.json`
- 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)
#### Settings UI Patterns:
There are two main patterns for saving settings in the OpenHands frontend:
**Pattern 1: Entity-based Resources (Immediate Save)**
- Used for: API Keys, Secrets, MCP Servers
- Behavior: Changes are saved immediately when user performs actions (add/edit/delete)
- Implementation:
- No "Save Changes" button
- No local state management or `isDirty` tracking
- Uses dedicated mutation hooks for each operation (e.g., `use-add-mcp-server.ts`, `use-delete-mcp-server.ts`)
- Each mutation triggers immediate API call with query invalidation for UI updates
- Example: MCP settings, API Keys & Secrets tabs
- Benefits: Simpler UX, no risk of losing changes, consistent with modern web app patterns
**Pattern 2: Form-based Settings (Manual Save)**
- Used for: Application settings, LLM configuration
- Behavior: Changes are accumulated locally and saved when user clicks "Save Changes"
- Implementation:
- Has "Save Changes" button that becomes enabled when changes are detected
- Uses local state management with `isDirty` tracking
- Uses `useSaveSettings` hook to save all changes at once
- Example: LLM tab, Application tab
- Benefits: Allows bulk changes, explicit save action, can validate all fields before saving
**When to use each pattern:**
- Use Pattern 1 (Immediate Save) for entity management where each item is independent
- Use Pattern 2 (Manual Save) for configuration forms where settings are interdependent or need validation
### Adding New LLM Models
To add a new LLM model to OpenHands, you need to update multiple files across both frontend and backend:
#### Model Configuration Procedure:
1. **Frontend Model Arrays** (`frontend/src/utils/verified-models.ts`):
- Add the model to `VERIFIED_MODELS` array (main list of all verified models)
- Add to provider-specific arrays based on the model's provider:
- `VERIFIED_OPENAI_MODELS` for OpenAI models
- `VERIFIED_ANTHROPIC_MODELS` for Anthropic models
- `VERIFIED_MISTRAL_MODELS` for Mistral models
- `VERIFIED_OPENHANDS_MODELS` for models available through OpenHands provider
2. **Backend CLI Integration** (`openhands/cli/utils.py`):
- Add the model to the appropriate `VERIFIED_*_MODELS` arrays
- This ensures the model appears in CLI model selection
3. **Backend Model List** (`openhands/utils/llm.py`):
- **CRITICAL**: Add the model to the `openhands_models` list (lines 57-66) if using OpenHands provider
- This is required for the model to appear in the frontend model selector
- Format: `'openhands/model-name'` (e.g., `'openhands/o3'`)
4. **Backend LLM Configuration** (`openhands/llm/llm.py`):
- Add to feature-specific arrays based on model capabilities:
- `FUNCTION_CALLING_SUPPORTED_MODELS` if the model supports function calling
- `REASONING_EFFORT_SUPPORTED_MODELS` if the model supports reasoning effort parameters
- `CACHE_PROMPT_SUPPORTED_MODELS` if the model supports prompt caching
- `MODELS_WITHOUT_STOP_WORDS` if the model doesn't support stop words
5. **Validation**:
- Run backend linting: `pre-commit run --config ./dev_config/python/.pre-commit-config.yaml`
- Run frontend linting: `cd frontend && npm run lint:fix`
- Run frontend build: `cd frontend && npm run build`
#### Model Verification Arrays:
- **VERIFIED_MODELS**: Main array of all verified models shown in the UI
- **VERIFIED_OPENAI_MODELS**: OpenAI models (LiteLLM doesn't return provider prefix)
- **VERIFIED_ANTHROPIC_MODELS**: Anthropic models (LiteLLM doesn't return provider prefix)
- **VERIFIED_MISTRAL_MODELS**: Mistral models (LiteLLM doesn't return provider prefix)
- **VERIFIED_OPENHANDS_MODELS**: Models available through OpenHands managed provider
#### Model Feature Support Arrays:
- **FUNCTION_CALLING_SUPPORTED_MODELS**: Models that support structured function calling
- **REASONING_EFFORT_SUPPORTED_MODELS**: Models that support reasoning effort parameters (like o1, o3)
- **CACHE_PROMPT_SUPPORTED_MODELS**: Models that support prompt caching for efficiency
- **MODELS_WITHOUT_STOP_WORDS**: Models that don't support stop word parameters
#### Frontend Model Integration:
- Models are automatically available in the model selector UI once added to verified arrays
- The `extractModelAndProvider` utility automatically detects provider from model arrays
- Provider-specific models are grouped and prioritized in the UI selection
#### CLI Model Integration:
- Models appear in CLI provider selection based on the verified arrays
- The `organize_models_and_providers` function groups models by provider
- Default model selection prioritizes verified models for each provider

1
CNAME
View File

@@ -1 +0,0 @@
docs.all-hands.dev

View File

@@ -61,7 +61,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
contact@openhands.dev.
contact@all-hands.dev.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -113,24 +113,19 @@ individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
### Slack Etiquettes
### Slack and Discord Etiquettes
These Slack etiquette guidelines are designed to foster an inclusive, respectful, and productive environment for all
community members. By following these best practices, we ensure effective communication and collaboration while
minimizing disruptions. Lets work together to build a supportive and welcoming community!
These Slack and Discord etiquette guidelines are designed to foster an inclusive, respectful, and productive environment for all community members. By following these best practices, we ensure effective communication and collaboration while minimizing disruptions. Lets work together to build a supportive and welcoming community!
- Communicate respectfully and professionally, avoiding sarcasm or harsh language, and remember that tone can be difficult to interpret in text.
- Use threads for specific discussions to keep channels organized and easier to follow.
- Tag others only when their input is critical or urgent, and use @here, @channel or @everyone sparingly to minimize disruptions.
- Be patient, as open-source contributors and maintainers often have other commitments and may need time to respond.
- Post questions or discussions in the most relevant channel (e.g., for [slack - #general](https://openhands-ai.slack.com/archives/C06P5NCGSFP) for general topics, [slack - #questions](https://openhands-ai.slack.com/archives/C06U8UTKSAD) for queries/questions.
- Post questions or discussions in the most relevant channel (e.g., for [slack - #general](https://openhands-ai.slack.com/archives/C06P5NCGSFP) for general topics, [slack - #questions](https://openhands-ai.slack.com/archives/C06U8UTKSAD) for queries/questions, [discord - #general](https://discord.com/channels/1222935860639563850/1222935861386018885)).
- When asking for help or raising issues, include necessary details like links, screenshots, or clear explanations to provide context.
- Keep discussions in public channels whenever possible to allow others to benefit from the conversation, unless the matter is sensitive or private.
- Always adhere to [our standards](https://github.com/OpenHands/OpenHands/blob/main/CODE_OF_CONDUCT.md#our-standards) to ensure a welcoming and collaborative environment.
- If you choose to mute a channel, consider setting up alerts for topics that still interest you to stay engaged.
For Slack, Go to Settings → Notifications → My Keywords to add specific keywords that will notify you when mentioned.
For example, if you're here for discussions about LLMs, mute the channel if its too busy, but set notifications to
alert you only when “LLMs” appears in messages.
- Always adhere to [our standards](https://github.com/All-Hands-AI/OpenHands/blob/main/CODE_OF_CONDUCT.md#our-standards) to ensure a welcoming and collaborative environment.
- If you choose to mute a channel, consider setting up alerts for topics that still interest you to stay engaged. For Slack, Go to Settings → Notifications → My Keywords to add specific keywords that will notify you when mentioned. For example, if you're here for discussions about LLMs, mute the channel if its too busy, but set notifications to alert you only when “LLMs” appears in messages. Also for Discord, go to the channel notifications and choose the option that best describes your need.
## Attribution

View File

@@ -1,58 +1,43 @@
# The OpenHands Community
# 🙌 The OpenHands Community
OpenHands is a community of engineers, academics, and enthusiasts reimagining software development for an AI-powered
world.
The OpenHands community is built around the belief that (1) AI and AI agents are going to fundamentally change the way
we build software, and (2) if this is true, we should do everything we can to make sure that the benefits provided by
such powerful technology are accessible to everyone.
## Mission
If this resonates with you, we'd love to have you join us in our quest!
Its very clear that AI is changing software development. We want the developer community to drive that change
organically, through open source.
## 🤝 How to Join
So were not just building friendly interfaces for AI-driven development. Were publishing _building blocks_ that
empower developers to create new experiences, tailored to your own habits, needs, and imagination.
Check out our [How to Join the Community section.](https://github.com/All-Hands-AI/OpenHands?tab=readme-ov-file#-how-to-join-the-community)
## Ethos
## 💪 Becoming a Contributor
We have two core values: **high openness** and **high agency**. While we dont expect everyone in the community to
embody these values, we want to establish them as norms.
We welcome contributions from everyone! Whether you're a developer, a researcher, or simply enthusiastic about advancing
the field of software engineering with AI, there are many ways to get involved:
### High Openness
- **Code Contributions:** Help us develop new core functionality, improve our agents, improve the frontend and other
interfaces, or anything else that would help make OpenHands better.
- **Research and Evaluation:** Contribute to our understanding of LLMs in software engineering, participate in
evaluating the models, or suggest improvements.
- **Feedback and Testing:** Use the OpenHands toolset, report bugs, suggest features, or provide feedback on usability.
We welcome anyone and everyone into our community by default. You dont have to be a software developer to help us
build. You dont have to be pro-AI to help us learn.
For details, please check [CONTRIBUTING.md](./CONTRIBUTING.md).
Our plans, our work, our successes, and our failures are all public record. We want the world to see not just the
fruits of our work, but the whole process of growing it.
## Code of Conduct
We welcome thoughtful criticism, whether its a comment on a PR or feedback on the community as a whole.
We have a [Code of Conduct](./CODE_OF_CONDUCT.md) that we expect all contributors to adhere to.
Long story short, we are aiming for an open, welcoming, diverse, inclusive, and healthy community.
All contributors are expected to contribute to building this sort of community.
### High Agency
## 🛠️ Becoming a Maintainer
Everyone should feel empowered to contribute to OpenHands. Whether its by making a PR, hosting an event, sharing
feedback, or just asking a question, dont hold back!
For contributors who have made significant and sustained contributions to the project, there is a possibility of joining
the maintainer team. The process for this is as follows:
OpenHands gives everyone the building blocks to create state-of-the-art developer experiences. We experiment constantly
and love building new things.
1. Any contributor who has made sustained and high-quality contributions to the codebase can be nominated by any
maintainer. If you feel that you may qualify you can reach out to any of the maintainers that have reviewed your PRs and ask if you can be nominated.
2. Once a maintainer nominates a new maintainer, there will be a discussion period among the maintainers for at least 3 days.
3. If no concerns are raised the nomination will be accepted by acclamation, and if concerns are raised there will be a discussion and possible vote.
Coding, development practices, and communities are changing rapidly. We wont hesitate to change direction and make big bets.
## Relationship to All Hands
OpenHands is supported by the for-profit organization [All Hands AI, Inc](https://www.openhands.dev/).
All Hands was founded by three of the first major contributors to OpenHands:
- Xingyao Wang, a UIUC PhD candidate who got OpenHands to the top of the SWE-bench leaderboards
- Graham Neubig, a CMU Professor who rallied the academic community around OpenHands
- Robert Brennan, a software engineer who architected the user-facing features of OpenHands
All Hands is an important part of the OpenHands ecosystem. Weve raised over $20M--mainly to hire developers and
researchers who can work on OpenHands full-time, and to provide them with expensive infrastructure. ([Join us!](https://allhandsai.applytojob.com/apply/))
But we see OpenHands as much larger, and ultimately more important, than All Hands. When our financial responsibility
to investors is at odds with our social responsibility to the community—as it inevitably will be, from time to time—we
promise to navigate that conflict thoughtfully and transparently.
At some point, we may transfer custody of OpenHands to an open source foundation. But for now,
the [Benevolent Dictator approach](http://www.catb.org/~esr/writings/cathedral-bazaar/homesteading/ar01s16.html) helps us move forward with speed and intention. If we ever forget the
“benevolent” part, please: fork us.
Note that just making many PRs does not immediately imply that you will become a maintainer. We will be looking
at sustained high-quality contributions over a period of time, as well as good teamwork and adherence to our [Code of Conduct](./CODE_OF_CONDUCT.md).

View File

@@ -6,41 +6,36 @@ Thanks for your interest in contributing to OpenHands! We welcome and appreciate
To understand the codebase, please refer to the README in each module:
- [frontend](./frontend/README.md)
- [evaluation](./evaluation/README.md)
- [openhands](./openhands/README.md)
- [agenthub](./openhands/agenthub/README.md)
- [server](./openhands/server/README.md)
For benchmarks and evaluation, see the [OpenHands/benchmarks](https://github.com/OpenHands/benchmarks) repository.
## Setting up Your Development Environment
We have a separate doc [Development.md](https://github.com/OpenHands/OpenHands/blob/main/Development.md) that tells
you how to set up a development workflow.
We have a separate doc [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md) that tells you how to set up a development workflow.
## How Can I Contribute?
There are many ways that you can contribute:
1. **Download and use** OpenHands, and send [issues](https://github.com/OpenHands/OpenHands/issues) when you encounter something that isn't working or a feature that you'd like to see.
2. **Send feedback** after each session by [clicking the thumbs-up thumbs-down buttons](https://docs.openhands.dev/usage/feedback), so we can see where things are working and failing, and also build an open dataset for training code agents.
3. **Improve the Codebase** by sending [PRs](#sending-pull-requests-to-openhands) (see details below). In particular, we have some [good first issues](https://github.com/OpenHands/OpenHands/labels/good%20first%20issue) that may be ones to start on.
1. **Download and use** OpenHands, and send [issues](https://github.com/All-Hands-AI/OpenHands/issues) when you encounter something that isn't working or a feature that you'd like to see.
2. **Send feedback** after each session by [clicking the thumbs-up thumbs-down buttons](https://docs.all-hands.dev/usage/feedback), so we can see where things are working and failing, and also build an open dataset for training code agents.
3. **Improve the Codebase** by sending [PRs](#sending-pull-requests-to-openhands) (see details below). In particular, we have some [good first issues](https://github.com/All-Hands-AI/OpenHands/labels/good%20first%20issue) that may be ones to start on.
## What Can I Build?
Here are a few ways you can help improve the codebase.
#### UI/UX
We're always looking to improve the look and feel of the application. If you've got a small fix
for something that's bugging you, feel free to open up a PR that changes the [`./frontend`](./frontend) directory.
If you're looking to make a bigger change, add a new UI element, or significantly alter the style
of the application, please open an issue first, or better, join the #dev-ui-ux channel in our Slack
of the application, please open an issue first, or better, join the #frontend channel in our Slack
to gather consensus from our design team first.
#### Improving the agent
Our main agent is the CodeAct agent. You can [see its prompts here](https://github.com/OpenHands/OpenHands/tree/main/openhands/agenthub/codeact_agent).
Our main agent is the CodeAct agent. You can [see its prompts here](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/agenthub/codeact_agent).
Changes to these prompts, and to the underlying behavior in Python, can have a huge impact on user experience.
You can try modifying the prompts to see how they change the behavior of the agent as you use the app
@@ -51,24 +46,19 @@ We use the [SWE-bench](https://www.swebench.com/) benchmark to test our agent. Y
channel in Slack to learn more.
#### Adding a new agent
You may want to experiment with building new types of agents. You can add an agent to [`openhands/agenthub`](./openhands/agenthub)
to help expand the capabilities of OpenHands.
#### Adding a new runtime
The agent needs a place to run code and commands. When you run OpenHands on your laptop, it uses a Docker container
to do this by default. But there are other ways of creating a sandbox for the agent.
If you work for a company that provides a cloud-based runtime, you could help us add support for that runtime
by implementing the [interface specified here](https://github.com/OpenHands/OpenHands/blob/main/openhands/runtime/base.py).
by implementing the [interface specified here](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/base.py).
#### Testing
When you write code, it is also good to write tests. Please navigate to the [`./tests`](./tests) folder to see existing
test suites. At the moment, we have these kinds of tests: [`unit`](./tests/unit), [`runtime`](./tests/runtime), and [`end-to-end (e2e)`](./tests/e2e).
Please refer to the README for each test suite. These tests also run on GitHub's continuous integration to ensure
quality of the project.
When you write code, it is also good to write tests. Please navigate to the [`./tests`](./tests) folder to see existing test suites.
At the moment, we have two kinds of tests: [`unit`](./tests/unit) and [`integration`](./evaluation/integration_tests). Please refer to the README for each test suite. These tests also run on GitHub's continuous integration to ensure quality of the project.
## Sending Pull Requests to OpenHands
@@ -76,8 +66,7 @@ You'll need to fork our repository to send us a Pull Request. You can learn more
about how to fork a GitHub repo and open a PR with your changes in [this article](https://medium.com/swlh/forks-and-pull-requests-how-to-contribute-to-github-repos-8843fac34ce8).
### Pull Request title
As described [here](https://github.com/commitizen/conventional-commit-types/blob/master/index.json), ideally a valid PR title should begin with one of the following prefixes:
As described [here](https://github.com/commitizen/conventional-commit-types/blob/master/index.json), a valid PR title should begin with one of the following prefixes:
- `feat`: A new feature
- `fix`: A bug fix
@@ -95,10 +84,9 @@ For example, a PR title could be:
- `refactor: modify package path`
- `feat(frontend): xxxx`, where `(frontend)` means that this PR mainly focuses on the frontend component.
You may also check out previous PRs in the [PR list](https://github.com/OpenHands/OpenHands/pulls).
You may also check out previous PRs in the [PR list](https://github.com/All-Hands-AI/OpenHands/pulls).
### Pull Request description
- If your PR is small (such as a typo fix), you can go brief.
- If it contains a lot of changes, it's better to write more details.
@@ -109,9 +97,7 @@ please include a short message that we can add to our changelog.
### Opening Issues
If you notice any bugs or have any feature requests please open them via the [issues page](https://github.com/OpenHands/OpenHands/issues). We will triage
based on how critical the bug is or how potentially useful the improvement is, discuss, and implement the ones that
the community has interest/effort for.
If you notice any bugs or have any feature requests please open them via the [issues page](https://github.com/All-Hands-AI/OpenHands/issues). We will triage based on how critical the bug is or how potentially useful the improvement is, discuss, and implement the ones that the community has interest/effort for.
Further, if you see an issue you like, please leave a "thumbs-up" or a comment, which will help us prioritize.
@@ -122,13 +108,11 @@ We're generally happy to consider all pull requests with the evaluation process
#### For Small Improvements
Small improvements with few downsides are typically reviewed and approved quickly.
One thing to check when making changes is to ensure that all continuous integration tests pass, which you can check
before getting a review.
One thing to check when making changes is to ensure that all continuous integration tests pass, which you can check before getting a review.
#### For Core Agent Changes
We need to be more careful with changes to the core agent, as it is imperative to maintain high quality. These PRs are
evaluated based on three key metrics:
We need to be more careful with changes to the core agent, as it is imperative to maintain high quality. These PRs are evaluated based on three key metrics:
1. **Accuracy**
2. **Efficiency**

View File

@@ -2,13 +2,11 @@
## Contributors
We would like to thank all the [contributors](https://github.com/OpenHands/OpenHands/graphs/contributors) who have
helped make OpenHands possible. We greatly appreciate your dedication and hard work.
We would like to thank all the [contributors](https://github.com/All-Hands-AI/OpenHands/graphs/contributors) who have helped make OpenHands possible. We greatly appreciate your dedication and hard work.
## Open Source Projects
OpenHands includes and adapts the following open source projects. We are grateful for their contributions to the
open source community:
OpenHands includes and adapts the following open source projects. We are grateful for their contributions to the open source community:
#### [SWE Agent](https://github.com/princeton-nlp/swe-agent)
- License: MIT License
@@ -16,14 +14,14 @@ open source community:
#### [Aider](https://github.com/paul-gauthier/aider)
- License: Apache License 2.0
- Description: AI pair programming tool. OpenHands has adapted and integrated its linter module for code-related tasks in [`agentskills utilities`](https://github.com/OpenHands/OpenHands/tree/main/openhands/runtime/plugins/agent_skills/utils/aider)
- Description: AI pair programming tool. OpenHands has adapted and integrated its linter module for code-related tasks in [`agentskills utilities`](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/runtime/plugins/agent_skills/utils/aider)
#### [BrowserGym](https://github.com/ServiceNow/BrowserGym)
- License: Apache License 2.0
- Description: Adapted in implementing the browsing agent
### Reference Implementations for Evaluation Benchmarks
### Reference Implementations for Evaluation Benchmarks
OpenHands integrates code of the reference implementations for the following agent evaluation benchmarks:
#### [HumanEval](https://github.com/openai/human-eval)
@@ -54,44 +52,28 @@ OpenHands integrates code of the reference implementations for the following age
#### [ProntoQA](https://github.com/asaparov/prontoqa)
- License: Apache License 2.0
## Open Source licenses
### MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### BSD 3-Clause License
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
### Apache License 2.0
@@ -286,6 +268,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright [yyyy] [name of copyright owner]
### Non-Open Source Reference Implementations:
#### [MultiPL-E](https://github.com/nuprl/MultiPL-E)

View File

@@ -2,7 +2,7 @@
This guide is for people working on OpenHands and editing the source code.
If you wish to contribute your changes, check out the
[CONTRIBUTING.md](https://github.com/OpenHands/OpenHands/blob/main/CONTRIBUTING.md)
[CONTRIBUTING.md](https://github.com/All-Hands-AI/OpenHands/blob/main/CONTRIBUTING.md)
on how to clone and setup the project initially before moving on. Otherwise,
you can clone the OpenHands project directly.
@@ -34,7 +34,7 @@ _Dev Container: Reopen in Container_ command from the Command Palette
#### Develop without sudo access
If you want to develop without system admin/sudo access to upgrade/install `Python` and/or `NodeJS`, you can use
If you want to develop without system admin/sudo access to upgrade/install `Python` and/or `NodeJs`, you can use
`conda` or `mamba` to manage the packages for you:
```bash
@@ -71,12 +71,12 @@ This command will prompt you to enter the LLM API key, model name, and other var
tailored to your specific needs. Note that the model name will apply only when you run headless. If you use the UI,
please set the model in the UI.
Note: If you have previously run OpenHands using the docker command, you may have already set some environment
Note: If you have previously run OpenHands using the docker command, you may have already set some environmental
variables in your terminal. The final configurations are set from highest to lowest priority:
Environment variables > config.toml variables > default variables
**Note on Alternative Models:**
See [our documentation](https://docs.openhands.dev/usage/llms) for recommended models.
See [our documentation](https://docs.all-hands.dev/usage/llms) for recommended models.
### 4. Running the application
@@ -91,43 +91,18 @@ make run
#### Option B: Individual Server Startup
- **Start the Backend Server:** If you prefer, you can start the backend server independently to focus on
backend-related tasks or configurations.
backend-related tasks or configurations.
```bash
make start-backend
```
- **Start the Frontend Server:** Similarly, you can start the frontend server on its own to work on frontend-related
components or interface enhancements.
components or interface enhancements.
```bash
make start-frontend
```
### 5. Running OpenHands with OpenHands
You can use OpenHands to develop and improve OpenHands itself! This is a powerful way to leverage AI assistance for contributing to the project.
#### Quick Start
1. **Build and run OpenHands:**
```bash
export INSTALL_DOCKER=0
export RUNTIME=local
make build && make run
```
2. **Access the interface:**
- Local development: http://localhost:3001
- Remote/cloud environments: Use the appropriate external URL
3. **Configure for external access (if needed):**
```bash
# For external access (e.g., cloud environments)
make run FRONTEND_PORT=12000 FRONTEND_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0
```
### 6. LLM Debugging
If you encounter any issues with the Language Model (LM) or you're simply curious, export DEBUG=1 in the environment and restart the backend.
@@ -156,12 +131,12 @@ poetry run pytest ./tests/unit/test_*.py
1. Add your dependency in `pyproject.toml` or use `poetry add xxx`.
2. Update the poetry.lock file via `poetry lock --no-update`.
### 10. Use existing Docker image
### 9. Use existing Docker image
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/openhands/runtime:1.2-nikolaik`
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.41-nikolaik`
## Develop inside Docker container
@@ -195,12 +170,12 @@ Here's a guide to the important documentation files in the repository:
- [/README.md](./README.md): Main project overview, features, and basic setup instructions
- [/Development.md](./Development.md) (this file): Comprehensive guide for developers working on OpenHands
- [/CONTRIBUTING.md](./CONTRIBUTING.md): Guidelines for contributing to the project, including code style and PR process
- [DOC_STYLE_GUIDE.md](https://github.com/OpenHands/docs/blob/main/openhands/DOC_STYLE_GUIDE.md): Standards for writing and maintaining project documentation
- [/docs/DOC_STYLE_GUIDE.md](./docs/DOC_STYLE_GUIDE.md): Standards for writing and maintaining project documentation
- [/openhands/README.md](./openhands/README.md): Details about the backend Python implementation
- [/frontend/README.md](./frontend/README.md): Frontend React application setup and development guide
- [/containers/README.md](./containers/README.md): Information about Docker containers and deployment
- [/tests/unit/README.md](./tests/unit/README.md): Guide to writing and running unit tests
- [OpenHands/benchmarks](https://github.com/OpenHands/benchmarks): Documentation for the evaluation framework and benchmarks
- [/skills/README.md](./skills/README.md): Information about the skills architecture and implementation
- [/evaluation/README.md](./evaluation/README.md): Documentation for the evaluation framework and benchmarks
- [/microagents/README.md](./microagents/README.md): Information about the microagents architecture and implementation
- [/openhands/server/README.md](./openhands/server/README.md): Server implementation details and API documentation
- [/openhands/runtime/README.md](./openhands/runtime/README.md): Documentation for the runtime environment and execution model

View File

@@ -3,14 +3,14 @@ These are the procedures and guidelines on how issues are triaged in this repo b
## General
* All issues must be tagged with **enhancement**, **bug** or **troubleshooting/help**.
* Issues may be tagged with what it relates to (**llm**, **app tab**, **UI/UX**, etc.).
* Issues may be tagged with what it relates to (**agent quality**, **resolver**, **CLI**, etc.).
## Severity
* **High**: High visibility issues or affecting many users.
* **Critical**: Affecting all users or potential security issues.
## Difficulty
* Issues good for newcomers may be tagged with **good first issue**.
* Issues with low implementation difficulty may be tagged with **good first issue**.
## Not Enough Information
* User is asked to provide more information (logs, how to reproduce, etc.) when the issue is not clear.
@@ -22,6 +22,6 @@ the issue may be closed as **not planned** (Usually after a week).
* Issues may be broken down into multiple issues if required.
## Stale and Auto Closures
* In order to keep a maintainable backlog, issues that have no activity within 40 days are automatically marked as **Stale**.
* If issues marked as **Stale** continue to have no activity for 10 more days, they will automatically be closed as not planned.
* In order to keep a maintainable backlog, issues that have no activity within 30 days are automatically marked as **Stale**.
* If issues marked as **Stale** continue to have no activity for 7 more days, they will automatically be closed as not planned.
* Issues may be reopened by maintainers if deemed important.

View File

@@ -1,12 +1,7 @@
Portions of this software are licensed as follows:
* All content that resides under the enterprise/ directory is licensed under the license defined in "enterprise/LICENSE".
* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below.
The MIT License (MIT)
=====================
The MIT License (MIT)
Copyright © 2025
Copyright © 2023
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View File

@@ -3,16 +3,15 @@ SHELL=/usr/bin/env bash
# Variables
BACKEND_HOST ?= "127.0.0.1"
BACKEND_PORT ?= 3000
BACKEND_PORT = 3000
BACKEND_HOST_PORT = "$(BACKEND_HOST):$(BACKEND_PORT)"
FRONTEND_HOST ?= "127.0.0.1"
FRONTEND_PORT ?= 3001
FRONTEND_PORT = 3001
DEFAULT_WORKSPACE_DIR = "./workspace"
DEFAULT_MODEL = "gpt-4o"
CONFIG_FILE = config.toml
PRE_COMMIT_CONFIG_PATH = "./dev_config/python/.pre-commit-config.yaml"
PYTHON_VERSION = 3.12
KIND_CLUSTER_NAME = "local-hands"
# ANSI color codes
GREEN=$(shell tput -Txterm setaf 2)
@@ -174,7 +173,7 @@ install-python-dependencies:
fi
@echo "$(GREEN)Python dependencies installed successfully.$(RESET)"
install-frontend-dependencies: check-npm check-nodejs
install-frontend-dependencies:
@echo "$(YELLOW)Setting up frontend environment...$(RESET)"
@echo "$(YELLOW)Detect Node.js version...$(RESET)"
@cd frontend && node ./scripts/detect-node-version.js
@@ -182,17 +181,17 @@ install-frontend-dependencies: check-npm check-nodejs
@cd frontend && npm install
@echo "$(GREEN)Frontend dependencies installed successfully.$(RESET)"
install-pre-commit-hooks: check-python check-poetry install-python-dependencies
install-pre-commit-hooks:
@echo "$(YELLOW)Installing pre-commit hooks...$(RESET)"
@git config --unset-all core.hooksPath || true
@poetry run pre-commit install --config $(PRE_COMMIT_CONFIG_PATH)
@echo "$(GREEN)Pre-commit hooks installed successfully.$(RESET)"
lint-backend: install-pre-commit-hooks
lint-backend:
@echo "$(YELLOW)Running linters...$(RESET)"
@poetry run pre-commit run --all-files --show-diff-on-failure --config $(PRE_COMMIT_CONFIG_PATH)
@poetry run pre-commit run --files openhands/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config $(PRE_COMMIT_CONFIG_PATH)
lint-frontend: install-frontend-dependencies
lint-frontend:
@echo "$(YELLOW)Running linters for frontend...$(RESET)"
@cd frontend && npm run lint
@@ -200,40 +199,6 @@ lint:
@$(MAKE) -s lint-frontend
@$(MAKE) -s lint-backend
kind:
@echo "$(YELLOW)Checking if kind is installed...$(RESET)"
@if ! command -v kind > /dev/null; then \
echo "$(RED)kind is not installed. Please install kind with `brew install kind` to continue$(RESET)"; \
exit 1; \
else \
echo "$(BLUE)kind $(shell kind version) is already installed.$(RESET)"; \
fi
@echo "$(YELLOW)Checking if kind cluster '$(KIND_CLUSTER_NAME)' already exists...$(RESET)"
@if kind get clusters | grep -q "^$(KIND_CLUSTER_NAME)$$"; then \
echo "$(BLUE)Kind cluster '$(KIND_CLUSTER_NAME)' already exists.$(RESET)"; \
kubectl config use-context kind-$(KIND_CLUSTER_NAME); \
else \
echo "$(YELLOW)Creating kind cluster '$(KIND_CLUSTER_NAME)'...$(RESET)"; \
kind create cluster --name $(KIND_CLUSTER_NAME) --config kind/cluster.yaml; \
fi
@echo "$(YELLOW)Checking if mirrord is installed...$(RESET)"
@if ! command -v mirrord > /dev/null; then \
echo "$(RED)mirrord is not installed. Please install mirrord with `brew install metalbear-co/mirrord/mirrord` to continue$(RESET)"; \
exit 1; \
else \
echo "$(BLUE)mirrord $(shell mirrord --version) is already installed.$(RESET)"; \
fi
@echo "$(YELLOW)Installing k8s mirrord resources...$(RESET)"
@kubectl apply -f kind/manifests
@echo "$(GREEN)Mirrord resources installed successfully.$(RESET)"
@echo "$(YELLOW)Waiting for Mirrord pod to be ready.$(RESET)"
@sleep 5
@kubectl wait --for=condition=Available deployment/ubuntu-dev
@echo "$(YELLOW)Waiting for Nginx to be ready.$(RESET)"
@kubectl -n ingress-nginx wait --for=condition=Available deployment/ingress-nginx-controller
@echo "$(YELLOW)Running make run inside of mirrord.$(RESET)"
@mirrord exec --target deployment/ubuntu-dev -- make run
test-frontend:
@echo "$(YELLOW)Running tests for frontend...$(RESET)"
@cd frontend && npm run test
@@ -368,4 +333,3 @@ help:
# Phony targets
.PHONY: build check-dependencies check-system check-python check-npm check-nodejs check-docker check-poetry install-python-dependencies install-frontend-dependencies install-pre-commit-hooks lint-backend lint-frontend lint test-frontend test build-frontend start-backend start-frontend _run_setup run run-wsl setup-config setup-config-prompts setup-config-basic openhands-cloud-run docker-dev docker-run clean help
.PHONY: kind

170
README.md
View File

@@ -1,86 +1,146 @@
<a name="readme-top"></a>
<div align="center">
<img src="https://raw.githubusercontent.com/OpenHands/docs/main/openhands/static/img/logo.png" alt="Logo" width="200">
<h1 align="center" style="border-bottom: none">OpenHands: AI-Driven Development</h1>
<img src="./docs/static/img/logo.png" alt="Logo" width="200">
<h1 align="center">OpenHands: Code Less, Make More</h1>
</div>
<div align="center">
<a href="https://github.com/OpenHands/OpenHands/blob/main/LICENSE"><img src="https://img.shields.io/badge/LICENSE-MIT-20B2AA?style=for-the-badge" alt="MIT License"></a>
<a href="https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=811504672#gid=811504672"><img src="https://img.shields.io/badge/SWEBench-77.6-00cc00?logoColor=FFE165&style=for-the-badge" alt="Benchmark Score"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/graphs/contributors"><img src="https://img.shields.io/github/contributors/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="Contributors"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/stargazers"><img src="https://img.shields.io/github/stars/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="Stargazers"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/blob/main/LICENSE"><img src="https://img.shields.io/github/license/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="MIT License"></a>
<br/>
<a href="https://docs.openhands.dev/sdk"><img src="https://img.shields.io/badge/Documentation-000?logo=googledocs&logoColor=FFE165&style=for-the-badge" alt="Check out the documentation"></a>
<a href="https://arxiv.org/abs/2511.03690"><img src="https://img.shields.io/badge/Paper-000?logoColor=FFE165&logo=arxiv&style=for-the-badge" alt="Tech Report"></a>
<!-- Keep these links. Translations will automatically update with the README. -->
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=de">Deutsch</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=es">Español</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=fr">français</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=ja">日本語</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=ko">한국어</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=pt">Português</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=ru">Русский</a> |
<a href="https://www.readme-i18n.com/OpenHands/OpenHands?lang=zh">中文</a>
<a href="https://join.slack.com/t/openhands-ai/shared_invite/zt-34zm4j0gj-Qz5kRHoca8DFCbqXPS~f_A"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="Join our Slack community"></a>
<a href="https://discord.gg/ESHStjSjD4"><img src="https://img.shields.io/badge/Discord-Join%20Us-purple?logo=discord&logoColor=white&style=for-the-badge" alt="Join our Discord community"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/blob/main/CREDITS.md"><img src="https://img.shields.io/badge/Project-Credits-blue?style=for-the-badge&color=FFE165&logo=github&logoColor=white" alt="Credits"></a>
<br/>
<a href="https://docs.all-hands.dev/usage/getting-started"><img src="https://img.shields.io/badge/Documentation-000?logo=googledocs&logoColor=FFE165&style=for-the-badge" alt="Check out the documentation"></a>
<a href="https://arxiv.org/abs/2407.16741"><img src="https://img.shields.io/badge/Paper%20on%20Arxiv-000?logoColor=FFE165&logo=arxiv&style=for-the-badge" alt="Paper on Arxiv"></a>
<a href="https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0#gid=0"><img src="https://img.shields.io/badge/Benchmark%20score-000?logoColor=FFE165&logo=huggingface&style=for-the-badge" alt="Evaluation Benchmark Score"></a>
<hr>
</div>
<hr>
Welcome to OpenHands (formerly OpenDevin), a platform for software development agents powered by AI.
🙌 Welcome to OpenHands, a [community](COMMUNITY.md) focused on AI-driven development. Wed love for you to [join us on Slack](https://dub.sh/openhands).
OpenHands agents can do anything a human developer can: modify code, run commands, browse the web,
call APIs, and yes—even copy code snippets from StackOverflow.
There are a few ways to work with OpenHands:
Learn more at [docs.all-hands.dev](https://docs.all-hands.dev), or [sign up for OpenHands Cloud](https://app.all-hands.dev) to get started.
### OpenHands Software Agent SDK
The SDK is a composable Python library that contains all of our agentic tech. It's the engine that powers everything else below.
> [!IMPORTANT]
> Using OpenHands for work? We'd love to chat! Fill out
> [this short form](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform)
> to join our Design Partner program, where you'll get early access to commercial features and the opportunity to provide input on our product roadmap.
Define agents in code, then run them locally, or scale to 1000s of agents in the cloud.
![App screenshot](./docs/static/img/screenshot.png)
[Check out the docs](https://docs.openhands.dev/sdk) or [view the source](https://github.com/OpenHands/software-agent-sdk/)
## ☁️ OpenHands Cloud
The easiest way to get started with OpenHands is on [OpenHands Cloud](https://app.all-hands.dev),
which comes with $50 in free credits for new users.
### OpenHands CLI
The CLI is the easiest way to start using OpenHands. The experience will be familiar to anyone who has worked
with e.g. Claude Code or Codex. You can power it with Claude, GPT, or any other LLM.
## 💻 Running OpenHands Locally
[Check out the docs](https://docs.openhands.dev/openhands/usage/run-openhands/cli-mode) or [view the source](https://github.com/OpenHands/OpenHands-CLI)
OpenHands can also run on your local system using Docker.
See the [Running OpenHands](https://docs.all-hands.dev/usage/installation) guide for
system requirements and more information.
### OpenHands Local GUI
Use the Local GUI for running agents on your laptop. It comes with a REST API and a single-page React application.
The experience will be familiar to anyone who has used Devin or Jules.
> [!WARNING]
> On a public network? See our [Hardened Docker Installation Guide](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)
> to secure your deployment by restricting network binding and implementing additional security measures.
[Check out the docs](https://docs.openhands.dev/openhands/usage/run-openhands/local-setup) or view the source in this repo.
### OpenHands Cloud
This is a deployment of OpenHands GUI, running on hosted infrastructure.
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
You can try it for free using the Minimax model by [signing in with your GitHub or GitLab account](https://app.all-hands.dev).
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-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
```
OpenHands Cloud comes with source-available features and integrations:
- Integrations with Slack, Jira, and Linear
- Multi-user support
- RBAC and permissions
- Collaboration features (e.g., conversation sharing)
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
### OpenHands Enterprise
Large enterprises can work with us to self-host OpenHands Cloud in their own VPC, via Kubernetes.
OpenHands Enterprise can also work with the CLI and SDK above.
When you open the application, you'll be asked to choose an LLM provider and add an API key.
[Anthropic's Claude Sonnet 4](https://www.anthropic.com/api) (`anthropic/claude-sonnet-4-20250514`)
works best, but you have [many options](https://docs.all-hands.dev/usage/llms).
OpenHands Enterprise is source-available--you can see all the source code here in the enterprise/ directory,
but you'll need to purchase a license if you want to run it for more than one month.
## 💡 Other ways to run OpenHands
Enterprise contracts also come with extended support and access to our research team.
> [!CAUTION]
> OpenHands is meant to be run by a single user on their local workstation.
> It is not appropriate for multi-tenant deployments where multiple users share the same instance. There is no built-in authentication, isolation, or scalability.
>
> If you're interested in running OpenHands in a multi-tenant environment, please
> [get in touch with us](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform)
> for advanced deployment options.
Learn more at [openhands.dev/enterprise](https://openhands.dev/enterprise)
You can also [connect OpenHands to your local filesystem](https://docs.all-hands.dev/usage/runtimes/docker#connecting-to-your-filesystem),
run OpenHands in a scriptable [headless mode](https://docs.all-hands.dev/usage/how-to/headless-mode),
interact with it via a [friendly CLI](https://docs.all-hands.dev/usage/how-to/cli-mode),
or run it on tagged issues with [a github action](https://docs.all-hands.dev/usage/how-to/github-action).
### Everything Else
Visit [Running OpenHands](https://docs.all-hands.dev/usage/installation) for more information and setup instructions.
Check out our [Product Roadmap](https://github.com/orgs/openhands/projects/1), and feel free to
[open up an issue](https://github.com/OpenHands/OpenHands/issues) if there's something you'd like to see!
If you want to modify the OpenHands source code, check out [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
You might also be interested in our [evaluation infrastructure](https://github.com/OpenHands/benchmarks), our [chrome extension](https://github.com/OpenHands/openhands-chrome-extension/), or our [Theory-of-Mind module](https://github.com/OpenHands/ToM-SWE).
Having issues? The [Troubleshooting Guide](https://docs.all-hands.dev/usage/troubleshooting) can help.
All our work is available under the MIT license, except for the `enterprise/` directory in this repository (see the [enterprise license](enterprise/LICENSE) for details).
The core `openhands` and `agent-server` Docker images are fully MIT-licensed as well.
## 📖 Documentation
<a href="https://deepwiki.com/All-Hands-AI/OpenHands"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki" title="Autogenerated Documentation by DeepWiki"></a>
If you need help with anything, or just want to chat, [come find us on Slack](https://dub.sh/openhands).
To learn more about the project, and for tips on using OpenHands,
check out our [documentation](https://docs.all-hands.dev/usage/getting-started).
There you'll find resources on how to use different LLM providers,
troubleshooting resources, and advanced configuration options.
## 🤝 How to Join the Community
OpenHands is a community-driven project, and we welcome contributions from everyone. We do most of our communication
through Slack, so this is the best place to start, but we also are happy to have you contact us on Discord or Github:
- [Join our Slack workspace](https://join.slack.com/t/openhands-ai/shared_invite/zt-34zm4j0gj-Qz5kRHoca8DFCbqXPS~f_A) - Here we talk about research, architecture, and future development.
- [Join our Discord server](https://discord.gg/ESHStjSjD4) - This is a community-run server for general discussion, questions, and feedback.
- [Read or post Github Issues](https://github.com/All-Hands-AI/OpenHands/issues) - Check out the issues we're working on, or add your own ideas.
See more about the community in [COMMUNITY.md](./COMMUNITY.md) or find details on contributing in [CONTRIBUTING.md](./CONTRIBUTING.md).
## 📈 Progress
See the monthly OpenHands roadmap [here](https://github.com/orgs/All-Hands-AI/projects/1) (updated at the maintainer's meeting at the end of each month).
<p align="center">
<a href="https://star-history.com/#All-Hands-AI/OpenHands&Date">
<img src="https://api.star-history.com/svg?repos=All-Hands-AI/OpenHands&type=Date" width="500" alt="Star History Chart">
</a>
</p>
## 📜 License
Distributed under the MIT License. See [`LICENSE`](./LICENSE) for more information.
## 🙏 Acknowledgements
OpenHands is built by a large number of contributors, and every contribution is greatly appreciated! We also build upon other open source projects, and we are deeply thankful for their work.
For a list of open source projects and licenses used in OpenHands, please see our [CREDITS.md](./CREDITS.md) file.
## 📚 Cite
```
@misc{openhands,
title={{OpenHands: An Open Platform for AI Software Developers as Generalist Agents}},
author={Xingyao Wang and Boxuan Li and Yufan Song and Frank F. Xu and Xiangru Tang and Mingchen Zhuge and Jiayi Pan and Yueqi Song and Bowen Li and Jaskirat Singh and Hoang H. Tran and Fuqiang Li and Ren Ma and Mingzhang Zheng and Bill Qian and Yanjun Shao and Niklas Muennighoff and Yizhe Zhang and Binyuan Hui and Junyang Lin and Robert Brennan and Hao Peng and Heng Ji and Graham Neubig},
year={2024},
eprint={2407.16741},
archivePrefix={arXiv},
primaryClass={cs.SE},
url={https://arxiv.org/abs/2407.16741},
}
```

146
README_CN.md Normal file
View File

@@ -0,0 +1,146 @@
<a name="readme-top"></a>
<div align="center">
<img src="./docs/static/img/logo.png" alt="Logo" width="200">
<h1 align="center">OpenHands: 少写代码,多做事</h1>
</div>
<div align="center">
<a href="https://github.com/All-Hands-AI/OpenHands/graphs/contributors"><img src="https://img.shields.io/github/contributors/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="Contributors"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/stargazers"><img src="https://img.shields.io/github/stars/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="Stargazers"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/blob/main/LICENSE"><img src="https://img.shields.io/github/license/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="MIT License"></a>
<br/>
<a href="https://join.slack.com/t/openhands-ai/shared_invite/zt-34zm4j0gj-Qz5kRHoca8DFCbqXPS~f_A"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="加入我们的Slack社区"></a>
<a href="https://discord.gg/ESHStjSjD4"><img src="https://img.shields.io/badge/Discord-Join%20Us-purple?logo=discord&logoColor=white&style=for-the-badge" alt="加入我们的Discord社区"></a>
<a href="https://github.com/All-Hands-AI/OpenHands/blob/main/CREDITS.md"><img src="https://img.shields.io/badge/Project-Credits-blue?style=for-the-badge&color=FFE165&logo=github&logoColor=white" alt="致谢"></a>
<br/>
<a href="https://docs.all-hands.dev/usage/getting-started"><img src="https://img.shields.io/badge/Documentation-000?logo=googledocs&logoColor=FFE165&style=for-the-badge" alt="查看文档"></a>
<a href="https://arxiv.org/abs/2407.16741"><img src="https://img.shields.io/badge/Paper%20on%20Arxiv-000?logoColor=FFE165&logo=arxiv&style=for-the-badge" alt="Arxiv论文"></a>
<a href="https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0#gid=0"><img src="https://img.shields.io/badge/Benchmark%20score-000?logoColor=FFE165&logo=huggingface&style=for-the-badge" alt="评估基准分数"></a>
<hr>
</div>
欢迎使用OpenHands前身为OpenDevin这是一个由AI驱动的软件开发代理平台。
OpenHands代理可以完成人类开发者能做的任何事情修改代码、运行命令、浏览网页、调用API甚至从StackOverflow复制代码片段。
在[docs.all-hands.dev](https://docs.all-hands.dev)了解更多信息,或[注册OpenHands Cloud](https://app.all-hands.dev)开始使用。
> [!IMPORTANT]
> 在工作中使用OpenHands我们很想与您交流填写
> [这份简短表格](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform)
> 加入我们的设计合作伙伴计划,您将获得商业功能的早期访问权限,并有机会对我们的产品路线图提供意见。
![应用截图](./docs/static/img/screenshot.png)
## ☁️ OpenHands Cloud
开始使用OpenHands的最简单方式是在[OpenHands Cloud](https://app.all-hands.dev)上,
新用户可获得$50的免费额度。
## 💻 在本地运行OpenHands
OpenHands也可以使用Docker在本地系统上运行。
查看[运行OpenHands](https://docs.all-hands.dev/usage/installation)指南了解
系统要求和更多信息。
> [!WARNING]
> 在公共网络上?请参阅我们的[强化Docker安装指南](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)
> 通过限制网络绑定和实施其他安全措施来保护您的部署。
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-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
```
您将在[http://localhost:3000](http://localhost:3000)找到运行中的OpenHands
打开应用程序时您将被要求选择一个LLM提供商并添加API密钥。
[Anthropic的Claude Sonnet 4](https://www.anthropic.com/api)`anthropic/claude-sonnet-4-20250514`
效果最佳,但您还有[许多选择](https://docs.all-hands.dev/usage/llms)。
## 💡 运行OpenHands的其他方式
> [!CAUTION]
> OpenHands旨在由单个用户在其本地工作站上运行。
> 它不适合多租户部署,即多个用户共享同一实例。没有内置的身份验证、隔离或可扩展性。
>
> 如果您有兴趣在多租户环境中运行OpenHands
> [与我们联系](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform)
> 了解高级部署选项。
您还可以[将OpenHands连接到本地文件系统](https://docs.all-hands.dev/usage/runtimes/docker#connecting-to-your-filesystem)
以可编程的[无头模式](https://docs.all-hands.dev/usage/how-to/headless-mode)运行OpenHands
通过[友好的CLI](https://docs.all-hands.dev/usage/how-to/cli-mode)与其交互,
或使用[GitHub Action](https://docs.all-hands.dev/usage/how-to/github-action)在标记的问题上运行它。
访问[运行OpenHands](https://docs.all-hands.dev/usage/installation)获取更多信息和设置说明。
如果您想修改OpenHands源代码请查看[Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md)。
遇到问题?[故障排除指南](https://docs.all-hands.dev/usage/troubleshooting)可以提供帮助。
## 📖 文档
<a href="https://deepwiki.com/All-Hands-AI/OpenHands"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki" title="DeepWiki自动生成文档"></a>
要了解有关项目的更多信息以及使用OpenHands的技巧
请查看我们的[文档](https://docs.all-hands.dev/usage/getting-started)。
在那里您将找到有关如何使用不同LLM提供商、
故障排除资源和高级配置选项的资源。
## 🤝 如何加入社区
OpenHands是一个社区驱动的项目我们欢迎每个人的贡献。我们大部分沟通
通过Slack进行因此这是开始的最佳场所但我们也很乐意您通过Discord或Github与我们联系
- [加入我们的Slack工作空间](https://join.slack.com/t/openhands-ai/shared_invite/zt-34zm4j0gj-Qz5kRHoca8DFCbqXPS~f_A) - 这里我们讨论研究、架构和未来发展。
- [加入我们的Discord服务器](https://discord.gg/ESHStjSjD4) - 这是一个社区运营的服务器,用于一般讨论、问题和反馈。
- [阅读或发布Github问题](https://github.com/All-Hands-AI/OpenHands/issues) - 查看我们正在处理的问题,或添加您自己的想法。
在[COMMUNITY.md](./COMMUNITY.md)中了解更多关于社区的信息,或在[CONTRIBUTING.md](./CONTRIBUTING.md)中找到有关贡献的详细信息。
## 📈 进展
在[这里](https://github.com/orgs/All-Hands-AI/projects/1)查看OpenHands月度路线图每月月底在维护者会议上更新
<p align="center">
<a href="https://star-history.com/#All-Hands-AI/OpenHands&Date">
<img src="https://api.star-history.com/svg?repos=All-Hands-AI/OpenHands&type=Date" width="500" alt="Star History Chart">
</a>
</p>
## 📜 许可证
根据MIT许可证分发。有关更多信息请参阅[`LICENSE`](./LICENSE)。
## 🙏 致谢
OpenHands由大量贡献者构建每一份贡献都备受感谢我们还借鉴了其他开源项目对他们的工作深表感谢。
有关OpenHands中使用的开源项目和许可证列表请参阅我们的[CREDITS.md](./CREDITS.md)文件。
## 📚 引用
```
@misc{openhands,
title={{OpenHands: An Open Platform for AI Software Developers as Generalist Agents}},
author={Xingyao Wang and Boxuan Li and Yufan Song and Frank F. Xu and Xiangru Tang and Mingchen Zhuge and Jiayi Pan and Yueqi Song and Bowen Li and Jaskirat Singh and Hoang H. Tran and Fuqiang Li and Ren Ma and Mingzhang Zheng and Bill Qian and Yanjun Shao and Niklas Muennighoff and Yizhe Zhang and Binyuan Hui and Junyang Lin and Robert Brennan and Hao Peng and Heng Ji and Graham Neubig},
year={2024},
eprint={2407.16741},
archivePrefix={arXiv},
primaryClass={cs.SE},
url={https://arxiv.org/abs/2407.16741},
}
```

View File

@@ -10,7 +10,18 @@
# General core configurations
##############################################################################
[core]
# API keys and configuration for core services
# API key for E2B
#e2b_api_key = ""
# API key for Modal
#modal_api_token_id = ""
#modal_api_token_secret = ""
# API key for Daytona
#daytona_api_key = ""
# Daytona Target
#daytona_target = ""
# Base path for the workspace
#workspace_base = "./workspace"
@@ -18,6 +29,9 @@
# Cache directory path
#cache_dir = "/tmp/cache"
# Reasoning effort for o1 models (low, medium, high, or not set)
#reasoning_effort = "medium"
# Debugging enabled
#debug = false
@@ -46,14 +60,11 @@
# Maximum file size for uploads, in megabytes
#file_uploads_max_file_size_mb = 0
# Enable the browser environment
#enable_browser = true
# Maximum budget per task, 0.0 means no limit
#max_budget_per_task = 0.0
# Maximum number of iterations
#max_iterations = 500
#max_iterations = 250
# Path to mount the workspace in the sandbox
#workspace_mount_path_in_sandbox = "/workspace"
@@ -116,9 +127,6 @@ api_key = ""
# API version
#api_version = ""
# Reasoning effort for OpenAI o-series models (low, medium, high, or not set)
#reasoning_effort = "medium"
# Cost per input token
#input_cost_per_token = 0.0
@@ -189,44 +197,15 @@ model = "gpt-4o"
# Whether to use native tool calling if supported by the model. Can be true, false, or None by default, which chooses the model's default behavior based on the evaluation.
# ATTENTION: Based on evaluation, enabling native function calling may lead to worse results
# in some scenarios. Use with caution and consider testing with your specific use case.
# https://github.com/OpenHands/OpenHands/pull/4711
# https://github.com/All-Hands-AI/OpenHands/pull/4711
#native_tool_calling = None
# Safety settings for models that support them (e.g., Mistral AI, Gemini)
# Example for Mistral AI:
# safety_settings = [
# { "category" = "hate", "threshold" = "low" },
# { "category" = "harassment", "threshold" = "low" },
# { "category" = "sexual", "threshold" = "low" },
# { "category" = "dangerous", "threshold" = "low" }
# ]
#
# Example for Gemini:
# safety_settings = [
# { "category" = "HARM_CATEGORY_HARASSMENT", "threshold" = "BLOCK_NONE" },
# { "category" = "HARM_CATEGORY_HATE_SPEECH", "threshold" = "BLOCK_NONE" },
# { "category" = "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold" = "BLOCK_NONE" },
# { "category" = "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold" = "BLOCK_NONE" }
# ]
#safety_settings = []
[llm.draft_editor]
# The number of times llm_editor tries to fix an error when editing.
correct_num = 5
[llm.gpt4o-mini]
api_key = ""
model = "gpt-4o"
# Example routing LLM configuration for multimodal model routing
# Uncomment and configure to enable model routing with a secondary model
#[llm.secondary_model]
#model = "kimi-k2"
#api_key = ""
#for_routing = true
#max_input_tokens = 128000
#################################### Agent ###################################
# Configuration for agents (group name starts with 'agent')
@@ -237,7 +216,6 @@ model = "gpt-4o"
[agent]
# Whether the browsing tool is enabled
# Note: when this is set to true, enable_browser in the core config must also be true
enable_browsing = true
# Whether the LLM draft editor is enabled
@@ -272,9 +250,6 @@ enable_finish = true
# length limit
enable_history_truncation = true
# Whether the condensation request tool is enabled
enable_condensation_request = false
[agent.RepoExplorerAgent]
# Example: use a cheaper model for RepoExplorerAgent to reduce cost, especially
# useful when an agent doesn't demand high quality but uses a lot of tokens
@@ -343,9 +318,6 @@ classpath = "my_package.my_module.MyCustomAgent"
# Enable GPU support in the runtime
#enable_gpu = false
# When there are multiple cards, you can specify the GPU by ID
#cuda_visible_devices = ''
# Additional Docker runtime kwargs
#docker_runtime_kwargs = {}
@@ -371,11 +343,10 @@ classpath = "my_package.my_module.MyCustomAgent"
#confirmation_mode = false
# The security analyzer to use (For Headless / CLI only - In Web this is overridden by Session Init)
# Available options: 'llm' (default), 'invariant'
#security_analyzer = "llm"
#security_analyzer = ""
# Whether to enable security analyzer
#enable_security_analyzer = true
#enable_security_analyzer = false
#################################### Condenser #################################
# Condensers control how conversation history is managed and compressed when
@@ -440,97 +411,7 @@ type = "noop"
#temperature = 0.1
#max_input_tokens = 1024
########################### Kubernetes #######################################
# Kubernetes configuration when using the Kubernetes runtime
#################################### Eval ####################################
# Configuration for the evaluation, please refer to the specific evaluation
# plugin for the available options
##############################################################################
[kubernetes]
# The Kubernetes namespace to use for OpenHands resources
#namespace = "default"
# Domain for ingress resources
#ingress_domain = "localhost"
# Size of the persistent volume claim
#pvc_storage_size = "2Gi"
# Storage class for persistent volume claims
#pvc_storage_class = "standard"
# CPU request for runtime pods
#resource_cpu_request = "1"
# Memory request for runtime pods
#resource_memory_request = "1Gi"
# Memory limit for runtime pods
#resource_memory_limit = "2Gi"
# Optional name of image pull secret for private registries
#image_pull_secret = ""
# Optional name of TLS secret for ingress
#ingress_tls_secret = ""
# Optional node selector key for pod scheduling
#node_selector_key = ""
# Optional node selector value for pod scheduling
#node_selector_val = ""
# Optional YAML string defining pod tolerations
#tolerations_yaml = ""
# Run the runtime sandbox container in privileged mode for use with docker-in-docker
#privileged = false
#################################### MCP #####################################
# Configuration for Model Context Protocol (MCP) servers
# MCP allows OpenHands to communicate with external tool servers
##############################################################################
[mcp]
# SSE servers - Server-Sent Events transport (legacy)
#sse_servers = [
# # Basic SSE server with just a URL
# "http://localhost:8080/mcp/sse",
#
# # SSE server with authentication
# {url = "https://api.example.com/mcp/sse", api_key = "your-api-key"}
#]
# SHTTP servers - Streamable HTTP transport (recommended)
#shttp_servers = [
# # Basic SHTTP server with default 60s timeout
# "https://api.example.com/mcp/shttp",
#
# # SHTTP server with custom timeout for long-running tools
# {
# url = "https://api.example.com/mcp/shttp",
# api_key = "your-api-key",
# timeout = 180 # 3 minutes for processing-heavy tools (1-3600 seconds)
# }
#]
# Stdio servers - Direct process communication (development only)
#stdio_servers = [
# # Basic stdio server
# {name = "filesystem", command = "npx", args = ["@modelcontextprotocol/server-filesystem", "/"]},
#
# # Stdio server with environment variables
# {
# name = "fetch",
# command = "uvx",
# args = ["mcp-server-fetch"],
# env = {DEBUG = "true"}
# }
#]
#################################### Model Routing ############################
# Configuration for experimental model routing feature
# Enables intelligent switching between different LLM models for specific purposes
##############################################################################
[model_routing]
# Router to use for model selection
# Available options:
# - "noop_router" (default): No routing, always uses primary LLM
# - "multimodal_router": A router that switches between primary and secondary models, depending on whether the input is multimodal or not
#router_name = "noop_router"

View File

@@ -1,16 +1,16 @@
ARG OPENHANDS_BUILD_VERSION=dev
FROM node:25.2-trixie-slim AS frontend-builder
FROM node:21.7.2-bookworm-slim AS frontend-builder
WORKDIR /app
COPY frontend/package.json frontend/package-lock.json ./
COPY ./frontend/package.json frontend/package-lock.json ./
RUN npm install -g npm@10.5.1
RUN npm ci
COPY frontend ./
COPY ./frontend ./
RUN npm run build
FROM python:3.13.7-slim-trixie AS base
FROM base AS backend-builder
FROM python:3.12.3-slim AS backend-builder
WORKDIR /app
ENV PYTHONPATH='/app'
@@ -21,19 +21,18 @@ ENV POETRY_NO_INTERACTION=1 \
POETRY_CACHE_DIR=/tmp/poetry_cache
RUN apt-get update -y \
&& apt-get install -y curl make git build-essential jq gettext \
&& python3 -m pip install poetry --break-system-packages
&& apt-get install -y curl make git build-essential \
&& python3 -m pip install poetry==1.8.2 --break-system-packages
COPY pyproject.toml poetry.lock ./
COPY ./pyproject.toml ./poetry.lock ./
RUN touch README.md
RUN export POETRY_CACHE_DIR && poetry install --no-root && rm -rf $POETRY_CACHE_DIR
FROM base AS openhands-app
FROM python:3.12.3-slim AS openhands-app
WORKDIR /app
# re-declare for this section
ARG OPENHANDS_BUILD_VERSION
ARG OPENHANDS_BUILD_VERSION #re-declare for this section
ENV RUN_AS_OPENHANDS=true
# A random number--we need this to be different from the user's UID on the host machine
@@ -44,8 +43,7 @@ ENV WORKSPACE_BASE=/opt/workspace_base
ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION
ENV SANDBOX_USER_ID=0
ENV FILE_STORE=local
ENV FILE_STORE_PATH=/.openhands
ENV INIT_GIT_IN_EMPTY_WORKSPACE=1
ENV FILE_STORE_PATH=/.openhands-state
RUN mkdir -p $FILE_STORE_PATH
RUN mkdir -p $WORKSPACE_BASE
@@ -58,34 +56,39 @@ RUN sed -i 's/^UID_MIN.*/UID_MIN 499/' /etc/login.defs
# Default is 60000, but we've seen up to 200000
RUN sed -i 's/^UID_MAX.*/UID_MAX 1000000/' /etc/login.defs
RUN groupadd --gid $OPENHANDS_USER_ID openhands
RUN useradd -l -m -u $OPENHANDS_USER_ID --gid $OPENHANDS_USER_ID -s /bin/bash openhands && \
usermod -aG openhands openhands && \
RUN groupadd app
RUN useradd -l -m -u $OPENHANDS_USER_ID -s /bin/bash openhands && \
usermod -aG app openhands && \
usermod -aG sudo openhands && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN chown -R openhands:openhands /app && chmod -R 770 /app
RUN sudo chown -R openhands:openhands $WORKSPACE_BASE && sudo chmod -R 770 $WORKSPACE_BASE
RUN chown -R openhands:app /app && chmod -R 770 /app
RUN sudo chown -R openhands:app $WORKSPACE_BASE && sudo chmod -R 770 $WORKSPACE_BASE
USER openhands
ENV VIRTUAL_ENV=/app/.venv \
PATH="/app/.venv/bin:$PATH" \
PYTHONPATH='/app'
COPY --chown=openhands:openhands --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
COPY --chown=openhands:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
COPY --chown=openhands:openhands --chmod=770 ./skills ./skills
COPY --chown=openhands:openhands --chmod=770 ./openhands ./openhands
COPY --chown=openhands:openhands --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
COPY --chown=openhands:openhands pyproject.toml poetry.lock README.md MANIFEST.in LICENSE ./
COPY --chown=openhands:app --chmod=770 ./microagents ./microagents
COPY --chown=openhands:app --chmod=770 ./openhands ./openhands
COPY --chown=openhands:app --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
COPY --chown=openhands:app --chmod=770 ./openhands/agenthub ./openhands/agenthub
COPY --chown=openhands:app ./pyproject.toml ./pyproject.toml
COPY --chown=openhands:app ./poetry.lock ./poetry.lock
COPY --chown=openhands:app ./README.md ./README.md
COPY --chown=openhands:app ./MANIFEST.in ./MANIFEST.in
COPY --chown=openhands:app ./LICENSE ./LICENSE
# This is run as "openhands" user, and will create __pycache__ with openhands:openhands ownership
RUN python openhands/core/download.py # No-op to download assets
# Add this line to set group ownership of all files/directories not already in "app" group
# openhands:openhands -> openhands:openhands
RUN find /app \! -group openhands -exec chgrp openhands {} +
# openhands:openhands -> openhands:app
RUN find /app \! -group app -exec chgrp app {} +
COPY --chown=openhands:openhands --chmod=770 --from=frontend-builder /app/build ./frontend/build
COPY --chown=openhands:openhands --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
COPY --chown=openhands:app --chmod=770 --from=frontend-builder /app/build ./frontend/build
COPY --chown=openhands:app --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
USER root

View File

@@ -1,4 +1,4 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=openhands
DOCKER_ORG=all-hands-ai
DOCKER_IMAGE=openhands
DOCKER_BASE_DIR="."

View File

@@ -23,18 +23,6 @@ if [ -z "$WORKSPACE_MOUNT_PATH" ]; then
unset WORKSPACE_BASE
fi
if [[ "$INSTALL_THIRD_PARTY_RUNTIMES" == "true" ]]; then
echo "Downloading and installing third_party_runtimes..."
echo "Warning: Third-party runtimes are provided as-is, not actively supported and may be removed in future releases."
if pip install 'openhands-ai[third_party_runtimes]' -qqq 2> >(tee /dev/stderr); then
echo "third_party_runtimes installed successfully."
else
echo "Failed to install third_party_runtimes." >&2
exit 1
fi
fi
if [[ "$SANDBOX_USER_ID" -eq 0 ]]; then
echo "Running OpenHands as root"
export RUN_AS_OPENHANDS=false
@@ -54,7 +42,7 @@ else
fi
fi
fi
usermod -aG openhands enduser
usermod -aG app enduser
# get the user group of /var/run/docker.sock and set openhands to that group
DOCKER_SOCKET_GID=$(stat -c '%g' /var/run/docker.sock)
echo "Docker socket group id: $DOCKER_SOCKET_GID"

View File

@@ -104,9 +104,6 @@ RUN apt-get update && apt-get install -y \
&& apt-get clean \
&& apt-get autoremove -y
# mark /app as safe git directory to avoid pre-commit errors
RUN git config --system --add safe.directory /app
WORKDIR /app
# cache build dependencies

View File

@@ -1,7 +1,7 @@
# Develop in Docker
> [!WARNING]
> This way of running OpenHands is not officially supported. It is maintained by the community and may not work.
> This is not officially supported and may not work.
Install [Docker](https://docs.docker.com/engine/install/) on your host machine and run:

View File

@@ -10,10 +10,8 @@ services:
environment:
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
- SANDBOX_API_HOSTNAME=host.docker.internal
- DOCKER_HOST_ADDR=host.docker.internal
#
- AGENT_SERVER_IMAGE_REPOSITORY=${AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server}
- AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-1.12.0-python}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.41-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:

View File

@@ -1,4 +1,4 @@
FROM ubuntu:24.04
FROM ubuntu:22.04
# install basic packages
RUN apt-get update && apt-get install -y \

View File

@@ -6,7 +6,7 @@ that depends on the `base_image` **AND** a [Python source distribution](https://
The following command will generate a `Dockerfile` file for `nikolaik/python-nodejs:python3.12-nodejs22` (the default base image), an updated `config.sh` and the runtime source distribution files/folders into `containers/runtime`:
```bash
poetry run python3 -m openhands.runtime.utils.runtime_build \
poetry run python3 openhands/runtime/utils/runtime_build.py \
--base_image nikolaik/python-nodejs:python3.12-nodejs22 \
--build_folder containers/runtime
```

View File

@@ -1,5 +1,5 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=openhands
DOCKER_ORG=all-hands-ai
DOCKER_BASE_DIR="./containers/runtime"
DOCKER_IMAGE=runtime
# These variables will be appended by the runtime_build.py script

View File

@@ -3,22 +3,12 @@ repos:
rev: v5.0.0
hooks:
- id: trailing-whitespace
exclude: ^(docs/|modules/|python/|openhands-ui/|third_party/|enterprise/)
exclude: docs/modules/python
- id: end-of-file-fixer
exclude: ^(docs/|modules/|python/|openhands-ui/|third_party/|enterprise/)
exclude: docs/modules/python
- id: check-yaml
args: ["--allow-multiple-documents"]
- id: debug-statements
- repo: local
hooks:
- id: warn-appmode-oss
name: "Warn on AppMode.OSS in backend (use AppMode.OPENHANDS)"
language: system
entry: bash -lc 'if rg -n "\\bAppMode\\.OSS\\b" openhands tests/unit; then echo "Found AppMode.OSS usage. Prefer AppMode.OPENHANDS."; exit 1; fi'
pass_filenames: false
- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.5.1
hooks:
@@ -37,28 +27,17 @@ repos:
entry: ruff check --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter]
args: [--fix, --unsafe-fixes]
exclude: ^(third_party/|enterprise/)
# Run the formatter.
- id: ruff-format
entry: ruff format --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter]
exclude: ^(third_party/|enterprise/)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0
hooks:
- id: mypy
additional_dependencies:
[
types-requests,
types-setuptools,
types-pyyaml,
types-toml,
types-docker,
types-Markdown,
pydantic,
lxml,
]
[types-requests, types-setuptools, types-pyyaml, types-toml, types-docker, lxml]
# To see gaps add `--html-report mypy-report/`
entry: mypy --config-file dev_config/python/mypy.ini openhands/
always_run: true

View File

@@ -7,10 +7,3 @@ warn_unreachable = True
warn_redundant_casts = True
no_implicit_optional = True
strict_optional = True
disable_error_code = type-abstract
# Exclude third-party runtime directory from type checking
exclude = (third_party/|enterprise/)
[mypy-openhands.memory.condenser.impl.*]
disable_error_code = override

View File

@@ -1,6 +1,3 @@
# Exclude third-party runtime directory from linting
exclude = ["third_party/", "enterprise/"]
[lint]
select = [
"E",

View File

@@ -7,9 +7,8 @@ services:
image: openhands:latest
container_name: openhands-app-${DATE:-}
environment:
- AGENT_SERVER_IMAGE_REPOSITORY=${AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server}
- AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-1.12.0-python}
#- 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 for this user
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.41-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:
- "3000:3000"
@@ -17,7 +16,7 @@ services:
- "host.docker.internal:host-gateway"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ~/.openhands:/.openhands
- ~/.openhands-state:/.openhands-state
- ${WORKSPACE_BASE:-$PWD/workspace}:/opt/workspace_base
pull_policy: build
stdin_open: true

67
docs/DOC_STYLE_GUIDE.md Normal file
View File

@@ -0,0 +1,67 @@
# Documentation Style Guide
## General Writing Principles
- **Clarity & Conciseness**: Always prioritize clarity and brevity. Avoid unnecessary jargon or overly complex explanations.
Keep sentences short and to the point.
- **Gradual Complexity**: Start with the simplest, most basic setup, and then gradually introduce more advanced
concepts and configurations.
## Formatting Guidelines
### Headers
Use **Title Case** for the first and second level headers.
Example:
- **Basic Usage**
- **Advanced Configuration Options**
### Lists
When listing items or options, use bullet points to enhance readability.
Example:
- Option A
- Option B
- Option C
### Procedures
For instructions or processes that need to be followed in a specific order, use numbered steps.
Example:
1. Step one: Do this.
- First this sub step.
- Then this sub step.
2. Step two: Complete this action.
3. Step three: Verify the result.
### Code Blocks
* Use code blocks for multi-line inputs, outputs, commands and code samples.
Example:
```bash
docker run -it \
-e THIS=this \
-e THAT=that
...
```
### Use of Note and Warning
When adding a note or warning, use the built-in note and warning syntax.
Example:
<Note>
This section is for advanced users only.
</Note>
### Referring to UI Elements
When referencing UI elements, use ``.
Example:
1. Toggle the `Advanced` option
2. Enter your model in the `Custom Model` textbox.

204
docs/docs.json Normal file
View File

@@ -0,0 +1,204 @@
{
"$schema": "https://mintlify.com/docs.json",
"theme": "mint",
"name": "All Hands Docs",
"colors": {
"primary": "#99873c",
"light": "#ffe165",
"dark": "#ffe165"
},
"background": {
"color": {
"light": "#f7f3ee",
"dark": "#0B0D0E"
}
},
"appearance": {
"default": "light"
},
"favicon": "/logo-square.png",
"navigation": {
"tabs": [
{
"tab": "Docs",
"pages": [
"index",
"usage/installation",
"usage/getting-started",
"usage/key-features",
{
"group": "OpenHands Cloud",
"pages": [
"usage/cloud/openhands-cloud",
{
"group": "Integrations",
"pages": [
"usage/cloud/github-installation",
"usage/cloud/gitlab-installation"
]
},
"usage/cloud/cloud-ui",
"usage/cloud/cloud-api"
]
},
{
"group": "Running OpenHands Locally",
"pages": [
"usage/local-setup",
"usage/how-to/gui-mode",
"usage/how-to/cli-mode",
"usage/how-to/headless-mode",
"usage/how-to/github-action"
]
},
{
"group": "Customization",
"pages": [
"usage/prompting/prompting-best-practices",
"usage/prompting/repository",
{
"group": "Microagents",
"pages": [
"usage/prompting/microagents-overview",
"usage/prompting/microagents-repo",
"usage/prompting/microagents-keyword",
"usage/prompting/microagents-org",
"usage/prompting/microagents-public"
]
}
]
},
{
"group": "Advanced Configuration",
"pages": [
{
"group": "LLM Configuration",
"pages": [
"usage/llms/llms",
{
"group": "Providers",
"pages": [
"usage/llms/azure-llms",
"usage/llms/google-llms",
"usage/llms/groq",
"usage/llms/local-llms",
"usage/llms/litellm-proxy",
"usage/llms/openai-llms",
"usage/llms/openrouter"
]
}
]
},
{
"group": "Runtime Configuration",
"pages": [
"usage/runtimes/overview",
{
"group": "Providers",
"pages": [
"usage/runtimes/docker",
"usage/runtimes/remote",
"usage/runtimes/local",
{
"group": "Third-Party Providers",
"pages": [
"usage/runtimes/modal",
"usage/runtimes/daytona",
"usage/runtimes/runloop",
"usage/runtimes/e2b"
]
}
]
}
]
},
"usage/configuration-options",
"usage/how-to/custom-sandbox-guide",
"usage/search-engine-setup",
"usage/mcp"
]
},
{
"group": "Troubleshooting & Feedback",
"pages": [
"usage/troubleshooting/troubleshooting",
"usage/feedback"
]
},
{
"group": "OpenHands Developers",
"pages": [
"usage/how-to/development-overview",
{
"group": "Architecture",
"pages": [
"usage/architecture/backend",
"usage/architecture/runtime"
]
},
"usage/how-to/debugging",
"usage/how-to/evaluation-harness",
"usage/how-to/websocket-connection"
]
}
]
},
{
"tab": "API Reference",
"openapi": "/openapi.json"
}
],
"global": {
"anchors": [
{
"anchor": "Company",
"href": "https://www.all-hands.dev/",
"icon": "house"
},
{
"anchor": "Blog",
"href": "https://www.all-hands.dev/blog",
"icon": "newspaper"
},
{
"anchor": "OpenHands Cloud",
"href": "https://app.all-hands.dev",
"icon": "cloud"
}
]
}
},
"logo": {
"light": "/logo/light.svg",
"dark": "/logo/dark.svg"
},
"navbar": {
"links": [
],
"primary": {
"type": "github",
"href": "https://github.com/All-Hands-AI/OpenHands"
}
},
"footer": {
"socials": {
"slack": "https://join.slack.com/t/openhands-ai/shared_invite/zt-34zm4j0gj-Qz5kRHoca8DFCbqXPS~f_A",
"github": "https://github.com/All-Hands-AI/OpenHands",
"discord": "https://discord.gg/ESHStjSjD4"
}
},
"contextual": {
"options": [
"copy",
"view",
"chatgpt",
"claude"
]
},
"redirects": [
{
"source": "/modules/:slug*",
"destination": "/:slug*"
}
]
}

19
docs/favicon.svg Normal file
View File

@@ -0,0 +1,19 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.06145 23.1079C5.26816 22.3769 -3.39077 20.6274 1.4173 5.06384C9.6344 6.09939 16.9728 14.0644 9.06145 23.1079Z" fill="url(#paint0_linear_17557_2021)"/>
<path d="M8.91928 23.0939C5.27642 21.2223 0.78371 4.20891 17.0071 0C20.7569 7.19341 19.6212 16.5452 8.91928 23.0939Z" fill="url(#paint1_linear_17557_2021)"/>
<path d="M8.91388 23.0788C8.73534 19.8817 10.1585 9.08525 23.5699 13.1107C23.1812 20.1229 18.984 26.4182 8.91388 23.0788Z" fill="url(#paint2_linear_17557_2021)"/>
<defs>
<linearGradient id="paint0_linear_17557_2021" x1="3.77557" y1="5.91571" x2="5.23185" y2="21.5589" gradientUnits="userSpaceOnUse">
<stop stop-color="#18E299"/>
<stop offset="1" stop-color="#15803D"/>
</linearGradient>
<linearGradient id="paint1_linear_17557_2021" x1="12.1711" y1="-0.718425" x2="10.1897" y2="22.9832" gradientUnits="userSpaceOnUse">
<stop stop-color="#16A34A"/>
<stop offset="1" stop-color="#4ADE80"/>
</linearGradient>
<linearGradient id="paint2_linear_17557_2021" x1="23.1327" y1="15.353" x2="9.33841" y2="18.5196" gradientUnits="userSpaceOnUse">
<stop stop-color="#4ADE80"/>
<stop offset="1" stop-color="#0D9373"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

16
docs/index.mdx Normal file
View File

@@ -0,0 +1,16 @@
---
title: Introduction
description: OpenHands - Code Less, Make More
icon: book-open
mode: wide
---
Use AI to tackle the toil in your backlog. Our agents have all the same tools as a human developer: they can modify code, run commands, browse the web, call APIs, and yes-even copy code snippets from StackOverflow.
<iframe
className="w-full aspect-video"
src="https://www.youtube.com/embed/oB4JR98KRAA"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>

BIN
docs/logo-square.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

29
docs/logo/dark.svg Normal file
View File

@@ -0,0 +1,29 @@
<svg width="1305" height="196" viewBox="0 0 1305 196" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_14027_2415)">
<g clip-path="url(#clip1_14027_2415)">
<path d="M282.419 58.7848C273.03 53.1628 266.757 61.779 267.526 73.4779L267.448 73.5663C267.474 61.3495 265.779 47.8566 260.145 36.9411C258.15 33.0751 254.107 26.7204 246.113 29.7146C242.605 31.0285 239.423 34.9828 241.066 45.1909C241.066 45.1909 242.892 55.9043 242.552 69.3719V69.5614C240.27 32.4182 231.663 21.0857 219.352 21.8185C215.414 22.5007 210.028 24.1557 211.841 35.5766C211.841 35.5766 213.81 47.4902 214.449 56.9782L214.488 57.4583H214.449C208.659 36.979 200.86 36.701 195.213 37.4969C190.088 38.2171 184.493 43.3969 187.323 53.605C196.204 85.6315 194.47 124.202 193.805 129.723C191.992 125.946 191.431 122.952 188.914 118.808C178.794 102.169 173.982 100.944 168.074 100.691C162.206 100.438 155.868 103.963 156.285 110.672C156.716 117.38 160.224 118.492 165.205 127.841C169.092 135.118 170.2 144.656 178.025 161.99C184.506 176.342 201.447 192.084 232.302 190.214C257.302 189.405 294.639 180.89 288.144 124.973C286.527 115.258 287.74 107.122 288.588 98.7833C289.905 85.8463 291.835 64.4068 282.432 58.7722L282.419 58.7848Z" fill="#FFE165"/>
<path d="M126.317 101.083C120.409 101.449 115.636 102.75 105.829 119.566C103.391 123.748 102.895 126.754 101.148 130.557C100.378 125.049 97.9134 86.516 106.181 54.3378C108.816 44.0918 103.13 39.0131 97.9916 38.3814C92.3318 37.6865 84.5201 38.1034 79.1211 58.7849H79.0559L79.1341 58.1911C79.5906 48.6905 81.3381 36.7516 81.3381 36.7516C82.9161 25.2928 77.517 23.7389 73.5655 23.1198C61.2808 22.6145 52.9083 33.9975 51.2912 70.8122H51.2652C50.7044 57.4836 52.3084 46.8713 52.3084 46.8713C53.756 36.6253 50.4957 32.7341 46.9616 31.4833C38.9152 28.6281 34.9898 35.0587 33.0727 38.9625C27.6476 49.9792 26.2 63.4973 26.4609 75.7141L26.3826 75.6257C26.9173 63.9142 20.488 55.4117 11.2027 61.1979C1.9174 66.9968 4.25176 88.3984 5.8167 101.31C6.83391 109.636 8.19019 117.747 6.75567 127.487C1.33054 183.505 38.8239 191.351 63.8368 191.717C94.7183 193.044 111.359 176.986 117.566 162.521C125.052 145.061 125.991 135.497 129.734 128.157C134.533 118.719 138.028 117.544 138.328 110.836C138.628 104.127 132.225 100.716 126.356 101.07L126.317 101.083Z" fill="#FFE165"/>
<path d="M138.679 101.171C135.549 98.2021 130.842 96.6228 126.056 96.9008C118.387 97.3682 112.949 100.084 104.59 113.35C104.368 98.1768 105.242 75.3981 110.38 55.361C112.31 47.8313 110.367 42.9168 108.385 40.1247C106.076 36.8652 102.49 34.7175 98.5256 34.2374C94.9132 33.7952 90.1923 33.7699 85.5888 37.4337C85.5888 37.3832 85.6018 37.32 85.6018 37.32C87.0885 26.5561 83.2544 20.3908 74.2169 19.0011L73.7213 18.9505C68.1527 18.7105 63.3797 20.4792 59.5325 24.1935C57.472 26.177 55.6854 28.7417 54.1335 31.9128C52.399 29.4492 50.169 28.1984 48.4084 27.5668C37.6495 23.7387 31.7027 31.938 29.1205 37.1684C26.1211 43.2579 24.2562 50.0801 23.2129 57.0413C22.9912 56.9023 22.7825 56.7634 22.5608 56.637C20.1873 55.3105 15.2447 53.6934 8.82849 57.6983C-2.04784 64.4952 -0.665478 84.027 1.49935 101.815C1.61673 102.763 1.7341 103.698 1.85147 104.645C2.77739 111.935 3.65115 118.82 2.45136 126.919L2.42528 127.121C0.247403 149.596 4.61619 166.892 15.4143 178.553C25.795 189.771 42.0574 195.608 63.6144 195.924C65.1924 195.987 66.7312 196.013 68.231 196C105.059 195.671 118.27 171.73 121.517 164.15C125.716 154.346 127.894 147.032 129.629 141.144C130.972 136.596 132.041 133.008 133.554 130.026C135.276 126.641 136.788 124.442 138.119 122.497C140.388 119.187 142.344 116.331 142.579 111.025C142.748 107.185 141.392 103.774 138.64 101.171H138.679ZM65.6488 30.1314C67.7224 28.1353 70.1089 27.2509 73.1345 27.3141C75.7557 27.731 78.1292 28.3501 77.0468 36.183C76.9686 36.6757 75.2732 48.4377 74.8168 58.0141C74.8168 58.0773 74.8168 58.1404 74.8168 58.2036C72.4302 67.5526 70.4349 81.0202 69.3656 100.704C64.7359 100.981 60.1194 101.462 55.6332 102.093C54.1856 62.158 57.5372 37.9391 65.6358 30.1314H65.6488ZM36.9452 40.7817C40.1925 34.2121 42.9181 34.5406 45.4481 35.4376C48.9561 36.6883 48.4084 43.4727 48.0041 46.29C47.9389 46.7448 46.374 57.3192 46.9217 70.7742C46.5174 80.2116 46.5696 91.0893 47.0521 103.534C42.7616 104.355 38.6927 105.302 34.976 106.326C33.2155 100.59 25.4299 64.1541 36.9452 40.7943V40.7817ZM130.946 117.86C129.537 119.907 127.79 122.459 125.821 126.325C123.956 129.976 122.808 133.892 121.335 138.832C119.652 144.505 117.553 151.567 113.549 160.929C110.706 167.549 98.7603 189.051 63.8752 187.535C44.4309 187.257 30.6855 182.482 21.8566 172.943C12.7539 163.114 9.11539 147.979 11.0324 127.98C12.3626 118.833 11.3846 111.088 10.4326 103.609C10.3152 102.674 10.1978 101.752 10.0804 100.817C9.03715 92.1884 6.24634 69.2581 13.5103 64.7226C15.4664 63.4972 17.0705 63.2192 18.2572 63.8762C20.2786 65.0006 22.313 69.0939 22.0261 75.436C22.0131 75.7772 22.0522 76.1056 22.1174 76.4341C22.4826 91.2156 25.2082 104.077 26.7601 108.903C24.2171 109.825 21.974 110.785 20.0961 111.745C17.9834 112.832 17.1879 115.359 18.3094 117.405C19.0919 118.833 20.6047 119.642 22.1696 119.629C22.8347 119.629 23.5128 119.465 24.1519 119.136C34.7022 113.716 59.7281 108.208 81.7807 108.802C84.1803 108.84 86.1495 107.046 86.2147 104.734C86.28 102.422 84.402 100.501 82.0155 100.438C80.6983 100.4 79.3681 100.4 78.0379 100.4C80.2419 60.7051 86.306 47.7555 91.0269 43.9275C92.9831 42.3483 94.848 42.2219 97.4301 42.5378C98.1474 42.6262 99.9471 43.0305 101.238 44.8497C102.634 46.8332 102.894 49.7643 101.982 53.327C94.0003 84.4186 95.8 120.981 96.7129 129.85C96.5564 130.153 96.4129 130.456 96.2434 130.772C94.3785 134.17 90.9096 137.708 86.7755 137.468C84.4151 137.354 82.3415 139.098 82.1981 141.397C82.0546 143.709 83.8673 145.692 86.2539 145.831C93.2439 146.236 99.8297 142.079 103.872 134.714C104.303 133.93 104.668 133.185 104.994 132.465C105.02 132.414 105.046 132.351 105.072 132.301C105.829 130.658 106.376 129.155 106.859 127.803C107.615 125.706 108.267 123.886 109.571 121.638C118.831 105.744 122.587 105.517 126.564 105.277C128.898 105.138 131.22 105.858 132.589 107.172C133.567 108.094 133.998 109.244 133.932 110.684C133.802 113.653 132.954 114.891 130.907 117.873L130.946 117.86Z" fill="black"/>
<path d="M292.383 124.316C291.04 116.243 291.783 109.345 292.566 102.043C292.67 101.095 292.774 100.16 292.866 99.2129C294.691 81.3993 295.709 61.8296 284.689 55.2348C278.194 51.3436 273.278 53.0492 270.93 54.4136C270.709 54.54 270.5 54.6916 270.278 54.8305C269.091 47.8946 267.109 41.1103 263.992 35.0713C261.319 29.8915 255.229 21.7933 244.535 25.8108C242.787 26.4678 240.597 27.7564 238.901 30.2579C237.284 27.1121 235.445 24.5853 233.346 22.6397C229.433 19.0012 224.621 17.3083 219.066 17.6494L218.57 17.6999C209.559 19.2539 205.842 25.4823 207.537 36.2463C207.537 36.2463 207.537 36.2968 207.55 36.3347C202.882 32.7467 198.161 32.8604 194.561 33.3658C190.61 33.9217 187.063 36.1326 184.82 39.43C182.902 42.2599 181.038 47.1997 183.111 54.6916C188.641 74.6403 189.945 97.4063 190.01 112.579C181.403 99.4656 175.912 96.8504 168.244 96.5219C163.471 96.3198 158.763 98.0001 155.699 101.02C152.999 103.673 151.708 107.109 151.956 110.937C152.295 116.231 154.303 119.061 156.638 122.32C158.007 124.24 159.559 126.413 161.345 129.761C162.923 132.718 164.058 136.28 165.493 140.803C167.344 146.653 169.653 153.93 174.048 163.658C177.438 171.175 191.105 194.888 227.829 194.547C229.316 194.535 230.855 194.484 232.42 194.383C254.094 193.688 270.239 187.548 280.411 176.152C290.975 164.302 295.017 146.931 292.422 124.493L292.396 124.291L292.383 124.316ZM245.317 44.5593C244.861 41.7041 244.17 34.9324 247.665 33.6311C250.169 32.6836 252.907 32.3172 256.272 38.8236C268.231 61.9812 261.149 98.5559 259.493 104.317C255.75 103.357 251.668 102.485 247.365 101.74C247.6 89.2954 247.443 78.4051 246.869 68.9803C247.156 55.5254 245.396 44.9762 245.317 44.5593ZM219.822 26.0003C222.861 25.874 225.26 26.7204 227.36 28.6913C235.602 36.36 239.423 60.5031 238.732 100.464C234.232 99.9078 229.616 99.5161 224.973 99.314C223.539 79.6432 221.283 66.2262 218.714 56.9151C218.714 56.8519 218.714 56.7888 218.714 56.7256C218.074 47.1492 216.144 35.4251 216.066 34.9703C214.827 27.1247 217.188 26.4678 219.809 26.0003H219.822ZM283.841 125.554C286.136 145.516 282.785 160.714 273.878 170.707C265.231 180.397 251.577 185.426 232.015 186.057C197.326 188.18 184.937 166.905 181.977 160.335C177.777 151.037 175.547 144.025 173.761 138.378C172.196 133.45 170.97 129.572 169.04 125.946C167.005 122.118 165.206 119.604 163.758 117.582C161.658 114.639 160.785 113.413 160.589 110.444C160.498 109.004 160.915 107.842 161.867 106.907C163.223 105.568 165.519 104.797 167.866 104.911C171.844 105.088 175.599 105.239 185.172 120.968C186.528 123.192 187.206 124.998 188.002 127.083C188.523 128.435 189.097 129.938 189.893 131.568C189.919 131.618 189.932 131.669 189.958 131.707C190.31 132.427 190.688 133.16 191.131 133.943C195.318 141.233 201.982 145.276 208.959 144.745C211.332 144.568 213.119 142.547 212.936 140.247C212.754 137.948 210.68 136.242 208.294 136.394C204.16 136.697 200.625 133.223 198.695 129.862C198.513 129.547 198.369 129.256 198.213 128.953C198.956 120.084 200.065 83.4839 191.484 52.5438C190.492 48.9937 190.701 46.0627 192.057 44.0539C193.322 42.2094 195.109 41.7672 195.826 41.6662C198.395 41.2998 200.273 41.4009 202.256 42.9422C207.055 46.6944 213.367 59.5303 216.314 99.175C214.984 99.1876 213.654 99.2255 212.349 99.2887C209.963 99.3898 208.124 101.348 208.228 103.66C208.333 105.972 210.315 107.715 212.741 107.652C234.767 106.667 259.911 111.72 270.552 116.963C271.191 117.279 271.869 117.418 272.547 117.418C274.112 117.406 275.612 116.572 276.368 115.119C277.451 113.06 276.603 110.533 274.464 109.484C272.573 108.549 270.304 107.64 267.748 106.755C269.209 101.904 271.7 88.9922 271.778 74.2107C271.843 73.8822 271.869 73.5537 271.843 73.2126C271.426 66.8831 273.395 62.7519 275.39 61.5896C276.564 60.9074 278.168 61.16 280.15 62.3476C287.506 66.7568 285.158 89.725 284.271 98.3791C284.18 99.314 284.076 100.236 283.971 101.171C283.163 108.676 282.328 116.42 283.841 125.554Z" fill="black"/>
<path d="M162.923 44.774C162.232 44.774 161.527 44.6477 160.862 44.3445C158.424 43.258 157.354 40.4659 158.476 38.1034C162.101 30.4347 167.409 23.3598 173.812 17.6494C175.781 15.8933 178.859 16.007 180.672 17.9273C182.485 19.835 182.367 22.8166 180.385 24.5727C174.921 29.4493 170.395 35.4882 167.292 42.0325C166.483 43.7381 164.749 44.7614 162.923 44.774Z" fill="black"/>
<path d="M146.57 42.0578C144.053 42.083 141.901 40.2132 141.692 37.737C140.766 26.6572 140.727 15.4258 141.601 4.34602C141.809 1.7561 144.144 -0.176866 146.804 0.0126405C149.478 0.214781 151.473 2.46359 151.277 5.05351C150.443 15.6406 150.482 26.3793 151.369 36.9664C151.59 39.5563 149.595 41.8304 146.922 42.0325C146.804 42.0325 146.687 42.0451 146.57 42.0451V42.0578Z" fill="black"/>
<path d="M129.851 44.9762C127.673 45.0015 125.665 43.5739 125.104 41.4388C123.043 33.6058 119.144 26.1645 113.836 19.9361C112.128 17.9273 112.428 14.971 114.488 13.316C116.562 11.661 119.614 11.9516 121.322 13.9477C127.53 21.25 132.094 29.9547 134.494 39.1141C135.159 41.6283 133.581 44.1929 130.985 44.8372C130.594 44.9383 130.216 44.9762 129.825 44.9888L129.851 44.9762Z" fill="black"/>
</g>
</g>
<path d="M379.045 147.144L419.52 55.0319C423.451 46.2048 424.882 39.0196 425.244 34.35C425.349 32.9943 426.419 31.9097 427.775 31.7892L440.789 30.5691C441.979 30.4637 443.109 31.1114 443.606 32.211L492.788 141.571C500.244 158.005 502.94 165.566 513.153 168.715C514.313 169.076 515.127 170.161 515.127 171.381V173.655C515.127 175.207 513.876 176.457 512.325 176.457H455.822C454.271 176.457 453.021 175.207 453.021 173.655V171.321C453.021 170.01 453.955 168.88 455.235 168.594C466.954 166.003 466.728 160.068 462.556 150.413L459.528 143.438C456.741 136.931 451.62 135.997 445.805 135.997H407.665C401.384 135.997 396.97 137.157 394.409 143.212L391.849 149.027C387.224 159.827 391.999 166.033 405.195 168.639C406.505 168.895 407.439 170.055 407.439 171.381V173.685C407.439 175.237 406.189 176.487 404.637 176.487H358.589C357.037 176.487 355.787 175.237 355.787 173.685V171.245C355.787 169.98 356.631 168.88 357.851 168.549C369.344 165.476 373.984 158.622 379.045 147.174V147.144ZM407.665 125.98H445.579C448.14 125.98 450.46 123.886 448.833 120.166L428.603 75.051C426.75 71.0894 425.816 71.0894 424.19 75.051L404.185 120.874C402.785 124.127 405.345 125.995 407.68 125.995L407.665 125.98Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M527.675 156.227V50.1663C527.675 40.5107 524.075 37.3023 515.685 37.4378C514.103 37.468 512.777 36.2328 512.777 34.6361V32.708C512.777 31.3523 513.741 30.2074 515.067 29.9514L545.344 24.3328C552.093 23.1729 555.572 26.2006 555.572 32.4669V156.212C555.572 162.825 558.329 166.184 563.179 165.883C564.836 165.777 566.267 166.997 566.267 168.669V168.745C566.267 169.227 566.162 169.709 565.921 170.115C562.682 175.689 555.316 177.843 548.598 177.843C536.502 177.843 527.66 171.094 527.66 156.212L527.675 156.227Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M581.748 156.227V50.1663C581.748 40.5107 578.148 37.3023 569.758 37.4378C568.176 37.468 566.851 36.2328 566.851 34.6361V32.708C566.851 31.3523 567.815 30.2074 569.14 29.9514L599.417 24.3328C606.166 23.1729 609.645 26.2006 609.645 32.4669V156.212C609.645 162.825 612.402 166.184 617.252 165.883C618.909 165.777 620.34 166.997 620.34 168.669V168.745C620.34 169.227 620.235 169.709 619.994 170.115C616.755 175.689 609.389 177.843 602.671 177.843C590.575 177.843 581.733 171.094 581.733 156.212L581.748 156.227Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M688.158 155.052V53.4201C688.158 45.2558 683.383 41.4599 674.405 39.818C673.079 39.577 672.115 38.4171 672.115 37.0765V34.817C672.115 33.2655 673.365 32.0152 674.917 32.0152H733.046C734.598 32.0152 735.848 33.2655 735.848 34.817V37.0765C735.848 38.4171 734.884 39.577 733.558 39.818C724.566 41.4448 719.806 45.2408 719.806 53.4201V92.9613C719.806 98.3087 723.993 100.644 729.808 100.644H770.283C776.097 100.644 780.285 98.3238 780.285 92.9613V53.4201C780.285 45.2558 775.51 41.4599 766.532 39.818C765.206 39.577 764.242 38.4171 764.242 37.0765V34.817C764.242 33.2655 765.493 32.0152 767.044 32.0152H825.173C826.725 32.0152 827.975 33.2655 827.975 34.817V37.0765C827.975 38.4171 827.011 39.577 825.686 39.818C816.708 41.4448 811.933 45.2408 811.933 53.4201V155.067C811.933 163.232 816.708 167.027 825.686 168.669C827.011 168.91 827.975 170.07 827.975 171.411V173.67C827.975 175.222 826.725 176.472 825.173 176.472H767.044C765.493 176.472 764.242 175.222 764.242 173.67V171.411C764.242 170.07 765.206 168.91 766.532 168.669C775.525 167.043 780.285 163.247 780.285 155.067V117.62C780.285 112.272 776.097 109.938 770.524 109.938H729.582C723.993 109.938 719.821 112.257 719.821 117.62V155.067C719.821 163.232 724.596 167.027 733.573 168.669C734.899 168.91 735.863 170.07 735.863 171.411V173.67C735.863 175.222 734.613 176.472 733.061 176.472H674.932C673.381 176.472 672.13 175.222 672.13 173.67V171.411C672.13 170.07 673.094 168.91 674.42 168.669C683.398 167.043 688.173 163.247 688.173 155.067L688.158 155.052Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M834.929 152.265C834.929 133.421 850.745 122.033 896.101 115.511C902.608 114.577 904.476 112.95 904.476 106.909V103.189C904.476 86.4384 899.128 77.8373 885.873 77.8373C859.361 77.8373 879.591 108.777 855.4 108.777C846.091 108.777 841.677 102.963 841.677 95.5216C841.677 77.6114 867.029 69.4772 892.621 69.4772C920.066 69.4772 932.388 79.4792 932.388 111.805V156.227C932.388 162.855 934.346 166.379 938.941 165.973C940.553 165.837 941.923 167.148 941.923 168.759C941.923 169.226 941.818 169.708 941.577 170.115C938.414 175.688 931.891 177.843 924.947 177.843C911.917 177.843 905.877 170.16 904.943 161.8C904.717 159.013 902.849 158.546 900.755 161.107C894.248 169.241 882.152 177.858 865.402 177.858C849.585 177.858 834.929 170.416 834.929 152.28V152.265ZM879.817 162.267C894.007 162.267 904.476 151.331 904.476 136.675V130.634C904.476 125.287 902.623 122.952 895.167 123.66C869.348 125.98 863.534 137.608 863.534 146.692C863.534 155.067 868.414 162.282 879.817 162.282V162.267Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M1038.21 156.227V114.125C1038.21 98.0829 1030.3 85.746 1015.89 85.746C1001.47 85.746 992.856 98.0678 992.856 114.125V154.359C992.856 164.211 996.2 167.751 1005.07 168.971C1006.46 169.167 1007.51 170.341 1007.51 171.742V173.655C1007.51 175.207 1006.26 176.457 1004.71 176.457H952.863C951.311 176.457 950.061 175.207 950.061 173.655V171.757C950.061 170.357 951.115 169.182 952.501 168.986C961.584 167.766 964.944 164.226 964.944 154.359V95.7481C964.944 86.0925 961.343 82.884 952.953 83.0196C951.372 83.0497 950.046 81.8145 950.046 80.2178V78.2897C950.046 76.934 951.01 75.7892 952.336 75.5331L982.613 69.9145C989.361 68.7546 992.841 71.7824 992.841 77.1298C992.841 85.505 996.561 87.5988 999.589 84.1041C1004.47 78.2897 1013.31 68.9806 1031.69 68.9806C1048.91 68.9806 1066.11 77.3558 1066.11 108.748V156.197C1066.11 162.81 1068.87 166.169 1073.72 165.868C1075.37 165.762 1076.8 166.982 1076.8 168.654V168.73C1076.8 169.212 1076.7 169.694 1076.46 170.1C1073.22 175.674 1065.85 177.828 1059.13 177.828C1047.04 177.828 1038.2 171.08 1038.2 156.197L1038.21 156.227Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M1082.63 125.061C1082.63 88.7735 1107.52 69.4775 1133.11 69.4775C1157.06 69.4775 1165.21 86.6948 1165.21 67.6096V50.1663C1165.21 40.5107 1161.61 37.3023 1153.22 37.4378C1151.64 37.468 1150.31 36.2328 1150.31 34.6361V32.708C1150.31 31.3523 1151.27 30.2074 1152.6 29.9514L1182.88 24.3328C1189.63 23.1729 1193.1 26.2006 1193.1 32.4669V156.212C1193.1 162.825 1195.86 166.184 1200.71 165.883C1202.37 165.777 1203.8 166.997 1203.8 168.669V168.745C1203.8 169.227 1203.69 169.709 1203.45 170.115C1200.21 175.689 1192.86 177.843 1186.36 177.843C1178.67 177.843 1170.77 174.815 1168.22 167.841C1166.82 164.12 1164.73 162.493 1162.18 162.493C1156.59 162.493 1148.46 177.843 1127.29 177.843C1104.04 177.843 1082.63 158.532 1082.63 125.046V125.061ZM1142.64 161.574C1152.64 161.574 1165.21 155.534 1165.21 135.289V117.62C1165.21 89.2405 1148.92 78.0786 1135.89 78.0786C1120.77 78.0786 1111.94 93.4281 1111.94 114.592C1111.94 139.718 1124.27 161.574 1142.64 161.574Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M1219.29 167.826C1218.9 167.539 1218.6 167.163 1218.39 166.726C1212.86 154.781 1212.35 141.103 1221.02 141.103C1224.28 141.103 1226.37 142.73 1227.53 146.225C1233.12 162.975 1242.88 169.483 1256.6 169.483C1272.19 169.483 1276.61 161.574 1276.61 154.133C1276.61 143.905 1267.07 140.636 1252.41 136.223C1233.57 130.408 1215.66 121.34 1215.66 101.095C1215.66 80.8501 1234.5 69.4623 1258.45 69.4623C1272.06 69.4623 1284.77 73.213 1292.72 78.7865C1293.13 79.0727 1293.46 79.4643 1293.67 79.9162C1299.19 91.8463 1299.68 105.268 1291.02 105.268C1287.77 105.268 1285.67 103.641 1284.51 100.146C1278.93 83.3958 1268.92 77.3554 1257.07 77.3554C1245.91 77.3554 1240.79 82.9439 1240.79 91.3041C1240.79 101.773 1249.63 106.895 1260.79 110.374C1277.54 115.496 1302.65 121.777 1302.65 145.035C1302.65 166.666 1280.33 177.828 1254.98 177.828C1240 177.828 1227.23 173.625 1219.28 167.795L1219.29 167.826Z" fill="#fff" stroke="#fff" stroke-width="0.393644" stroke-miterlimit="10"/>
<defs>
<clipPath id="clip0_14027_2415">
<rect width="294" height="196" fill="white"/>
</clipPath>
<clipPath id="clip1_14027_2415">
<rect width="294" height="196" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

29
docs/logo/light.svg Normal file
View File

@@ -0,0 +1,29 @@
<svg width="1305" height="196" viewBox="0 0 1305 196" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_14027_2415)">
<g clip-path="url(#clip1_14027_2415)">
<path d="M282.419 58.7848C273.03 53.1628 266.757 61.779 267.526 73.4779L267.448 73.5663C267.474 61.3495 265.779 47.8566 260.145 36.9411C258.15 33.0751 254.107 26.7204 246.113 29.7146C242.605 31.0285 239.423 34.9828 241.066 45.1909C241.066 45.1909 242.892 55.9043 242.552 69.3719V69.5614C240.27 32.4182 231.663 21.0857 219.352 21.8185C215.414 22.5007 210.028 24.1557 211.841 35.5766C211.841 35.5766 213.81 47.4902 214.449 56.9782L214.488 57.4583H214.449C208.659 36.979 200.86 36.701 195.213 37.4969C190.088 38.2171 184.493 43.3969 187.323 53.605C196.204 85.6315 194.47 124.202 193.805 129.723C191.992 125.946 191.431 122.952 188.914 118.808C178.794 102.169 173.982 100.944 168.074 100.691C162.206 100.438 155.868 103.963 156.285 110.672C156.716 117.38 160.224 118.492 165.205 127.841C169.092 135.118 170.2 144.656 178.025 161.99C184.506 176.342 201.447 192.084 232.302 190.214C257.302 189.405 294.639 180.89 288.144 124.973C286.527 115.258 287.74 107.122 288.588 98.7833C289.905 85.8463 291.835 64.4068 282.432 58.7722L282.419 58.7848Z" fill="#FFE165"/>
<path d="M126.317 101.083C120.409 101.449 115.636 102.75 105.829 119.566C103.391 123.748 102.895 126.754 101.148 130.557C100.378 125.049 97.9134 86.516 106.181 54.3378C108.816 44.0918 103.13 39.0131 97.9916 38.3814C92.3318 37.6865 84.5201 38.1034 79.1211 58.7849H79.0559L79.1341 58.1911C79.5906 48.6905 81.3381 36.7516 81.3381 36.7516C82.9161 25.2928 77.517 23.7389 73.5655 23.1198C61.2808 22.6145 52.9083 33.9975 51.2912 70.8122H51.2652C50.7044 57.4836 52.3084 46.8713 52.3084 46.8713C53.756 36.6253 50.4957 32.7341 46.9616 31.4833C38.9152 28.6281 34.9898 35.0587 33.0727 38.9625C27.6476 49.9792 26.2 63.4973 26.4609 75.7141L26.3826 75.6257C26.9173 63.9142 20.488 55.4117 11.2027 61.1979C1.9174 66.9968 4.25176 88.3984 5.8167 101.31C6.83391 109.636 8.19019 117.747 6.75567 127.487C1.33054 183.505 38.8239 191.351 63.8368 191.717C94.7183 193.044 111.359 176.986 117.566 162.521C125.052 145.061 125.991 135.497 129.734 128.157C134.533 118.719 138.028 117.544 138.328 110.836C138.628 104.127 132.225 100.716 126.356 101.07L126.317 101.083Z" fill="#FFE165"/>
<path d="M138.679 101.171C135.549 98.2021 130.842 96.6228 126.056 96.9008C118.387 97.3682 112.949 100.084 104.59 113.35C104.368 98.1768 105.242 75.3981 110.38 55.361C112.31 47.8313 110.367 42.9168 108.385 40.1247C106.076 36.8652 102.49 34.7175 98.5256 34.2374C94.9132 33.7952 90.1923 33.7699 85.5888 37.4337C85.5888 37.3832 85.6018 37.32 85.6018 37.32C87.0885 26.5561 83.2544 20.3908 74.2169 19.0011L73.7213 18.9505C68.1527 18.7105 63.3797 20.4792 59.5325 24.1935C57.472 26.177 55.6854 28.7417 54.1335 31.9128C52.399 29.4492 50.169 28.1984 48.4084 27.5668C37.6495 23.7387 31.7027 31.938 29.1205 37.1684C26.1211 43.2579 24.2562 50.0801 23.2129 57.0413C22.9912 56.9023 22.7825 56.7634 22.5608 56.637C20.1873 55.3105 15.2447 53.6934 8.82849 57.6983C-2.04784 64.4952 -0.665478 84.027 1.49935 101.815C1.61673 102.763 1.7341 103.698 1.85147 104.645C2.77739 111.935 3.65115 118.82 2.45136 126.919L2.42528 127.121C0.247403 149.596 4.61619 166.892 15.4143 178.553C25.795 189.771 42.0574 195.608 63.6144 195.924C65.1924 195.987 66.7312 196.013 68.231 196C105.059 195.671 118.27 171.73 121.517 164.15C125.716 154.346 127.894 147.032 129.629 141.144C130.972 136.596 132.041 133.008 133.554 130.026C135.276 126.641 136.788 124.442 138.119 122.497C140.388 119.187 142.344 116.331 142.579 111.025C142.748 107.185 141.392 103.774 138.64 101.171H138.679ZM65.6488 30.1314C67.7224 28.1353 70.1089 27.2509 73.1345 27.3141C75.7557 27.731 78.1292 28.3501 77.0468 36.183C76.9686 36.6757 75.2732 48.4377 74.8168 58.0141C74.8168 58.0773 74.8168 58.1404 74.8168 58.2036C72.4302 67.5526 70.4349 81.0202 69.3656 100.704C64.7359 100.981 60.1194 101.462 55.6332 102.093C54.1856 62.158 57.5372 37.9391 65.6358 30.1314H65.6488ZM36.9452 40.7817C40.1925 34.2121 42.9181 34.5406 45.4481 35.4376C48.9561 36.6883 48.4084 43.4727 48.0041 46.29C47.9389 46.7448 46.374 57.3192 46.9217 70.7742C46.5174 80.2116 46.5696 91.0893 47.0521 103.534C42.7616 104.355 38.6927 105.302 34.976 106.326C33.2155 100.59 25.4299 64.1541 36.9452 40.7943V40.7817ZM130.946 117.86C129.537 119.907 127.79 122.459 125.821 126.325C123.956 129.976 122.808 133.892 121.335 138.832C119.652 144.505 117.553 151.567 113.549 160.929C110.706 167.549 98.7603 189.051 63.8752 187.535C44.4309 187.257 30.6855 182.482 21.8566 172.943C12.7539 163.114 9.11539 147.979 11.0324 127.98C12.3626 118.833 11.3846 111.088 10.4326 103.609C10.3152 102.674 10.1978 101.752 10.0804 100.817C9.03715 92.1884 6.24634 69.2581 13.5103 64.7226C15.4664 63.4972 17.0705 63.2192 18.2572 63.8762C20.2786 65.0006 22.313 69.0939 22.0261 75.436C22.0131 75.7772 22.0522 76.1056 22.1174 76.4341C22.4826 91.2156 25.2082 104.077 26.7601 108.903C24.2171 109.825 21.974 110.785 20.0961 111.745C17.9834 112.832 17.1879 115.359 18.3094 117.405C19.0919 118.833 20.6047 119.642 22.1696 119.629C22.8347 119.629 23.5128 119.465 24.1519 119.136C34.7022 113.716 59.7281 108.208 81.7807 108.802C84.1803 108.84 86.1495 107.046 86.2147 104.734C86.28 102.422 84.402 100.501 82.0155 100.438C80.6983 100.4 79.3681 100.4 78.0379 100.4C80.2419 60.7051 86.306 47.7555 91.0269 43.9275C92.9831 42.3483 94.848 42.2219 97.4301 42.5378C98.1474 42.6262 99.9471 43.0305 101.238 44.8497C102.634 46.8332 102.894 49.7643 101.982 53.327C94.0003 84.4186 95.8 120.981 96.7129 129.85C96.5564 130.153 96.4129 130.456 96.2434 130.772C94.3785 134.17 90.9096 137.708 86.7755 137.468C84.4151 137.354 82.3415 139.098 82.1981 141.397C82.0546 143.709 83.8673 145.692 86.2539 145.831C93.2439 146.236 99.8297 142.079 103.872 134.714C104.303 133.93 104.668 133.185 104.994 132.465C105.02 132.414 105.046 132.351 105.072 132.301C105.829 130.658 106.376 129.155 106.859 127.803C107.615 125.706 108.267 123.886 109.571 121.638C118.831 105.744 122.587 105.517 126.564 105.277C128.898 105.138 131.22 105.858 132.589 107.172C133.567 108.094 133.998 109.244 133.932 110.684C133.802 113.653 132.954 114.891 130.907 117.873L130.946 117.86Z" fill="black"/>
<path d="M292.383 124.316C291.04 116.243 291.783 109.345 292.566 102.043C292.67 101.095 292.774 100.16 292.866 99.2129C294.691 81.3993 295.709 61.8296 284.689 55.2348C278.194 51.3436 273.278 53.0492 270.93 54.4136C270.709 54.54 270.5 54.6916 270.278 54.8305C269.091 47.8946 267.109 41.1103 263.992 35.0713C261.319 29.8915 255.229 21.7933 244.535 25.8108C242.787 26.4678 240.597 27.7564 238.901 30.2579C237.284 27.1121 235.445 24.5853 233.346 22.6397C229.433 19.0012 224.621 17.3083 219.066 17.6494L218.57 17.6999C209.559 19.2539 205.842 25.4823 207.537 36.2463C207.537 36.2463 207.537 36.2968 207.55 36.3347C202.882 32.7467 198.161 32.8604 194.561 33.3658C190.61 33.9217 187.063 36.1326 184.82 39.43C182.902 42.2599 181.038 47.1997 183.111 54.6916C188.641 74.6403 189.945 97.4063 190.01 112.579C181.403 99.4656 175.912 96.8504 168.244 96.5219C163.471 96.3198 158.763 98.0001 155.699 101.02C152.999 103.673 151.708 107.109 151.956 110.937C152.295 116.231 154.303 119.061 156.638 122.32C158.007 124.24 159.559 126.413 161.345 129.761C162.923 132.718 164.058 136.28 165.493 140.803C167.344 146.653 169.653 153.93 174.048 163.658C177.438 171.175 191.105 194.888 227.829 194.547C229.316 194.535 230.855 194.484 232.42 194.383C254.094 193.688 270.239 187.548 280.411 176.152C290.975 164.302 295.017 146.931 292.422 124.493L292.396 124.291L292.383 124.316ZM245.317 44.5593C244.861 41.7041 244.17 34.9324 247.665 33.6311C250.169 32.6836 252.907 32.3172 256.272 38.8236C268.231 61.9812 261.149 98.5559 259.493 104.317C255.75 103.357 251.668 102.485 247.365 101.74C247.6 89.2954 247.443 78.4051 246.869 68.9803C247.156 55.5254 245.396 44.9762 245.317 44.5593ZM219.822 26.0003C222.861 25.874 225.26 26.7204 227.36 28.6913C235.602 36.36 239.423 60.5031 238.732 100.464C234.232 99.9078 229.616 99.5161 224.973 99.314C223.539 79.6432 221.283 66.2262 218.714 56.9151C218.714 56.8519 218.714 56.7888 218.714 56.7256C218.074 47.1492 216.144 35.4251 216.066 34.9703C214.827 27.1247 217.188 26.4678 219.809 26.0003H219.822ZM283.841 125.554C286.136 145.516 282.785 160.714 273.878 170.707C265.231 180.397 251.577 185.426 232.015 186.057C197.326 188.18 184.937 166.905 181.977 160.335C177.777 151.037 175.547 144.025 173.761 138.378C172.196 133.45 170.97 129.572 169.04 125.946C167.005 122.118 165.206 119.604 163.758 117.582C161.658 114.639 160.785 113.413 160.589 110.444C160.498 109.004 160.915 107.842 161.867 106.907C163.223 105.568 165.519 104.797 167.866 104.911C171.844 105.088 175.599 105.239 185.172 120.968C186.528 123.192 187.206 124.998 188.002 127.083C188.523 128.435 189.097 129.938 189.893 131.568C189.919 131.618 189.932 131.669 189.958 131.707C190.31 132.427 190.688 133.16 191.131 133.943C195.318 141.233 201.982 145.276 208.959 144.745C211.332 144.568 213.119 142.547 212.936 140.247C212.754 137.948 210.68 136.242 208.294 136.394C204.16 136.697 200.625 133.223 198.695 129.862C198.513 129.547 198.369 129.256 198.213 128.953C198.956 120.084 200.065 83.4839 191.484 52.5438C190.492 48.9937 190.701 46.0627 192.057 44.0539C193.322 42.2094 195.109 41.7672 195.826 41.6662C198.395 41.2998 200.273 41.4009 202.256 42.9422C207.055 46.6944 213.367 59.5303 216.314 99.175C214.984 99.1876 213.654 99.2255 212.349 99.2887C209.963 99.3898 208.124 101.348 208.228 103.66C208.333 105.972 210.315 107.715 212.741 107.652C234.767 106.667 259.911 111.72 270.552 116.963C271.191 117.279 271.869 117.418 272.547 117.418C274.112 117.406 275.612 116.572 276.368 115.119C277.451 113.06 276.603 110.533 274.464 109.484C272.573 108.549 270.304 107.64 267.748 106.755C269.209 101.904 271.7 88.9922 271.778 74.2107C271.843 73.8822 271.869 73.5537 271.843 73.2126C271.426 66.8831 273.395 62.7519 275.39 61.5896C276.564 60.9074 278.168 61.16 280.15 62.3476C287.506 66.7568 285.158 89.725 284.271 98.3791C284.18 99.314 284.076 100.236 283.971 101.171C283.163 108.676 282.328 116.42 283.841 125.554Z" fill="black"/>
<path d="M162.923 44.774C162.232 44.774 161.527 44.6477 160.862 44.3445C158.424 43.258 157.354 40.4659 158.476 38.1034C162.101 30.4347 167.409 23.3598 173.812 17.6494C175.781 15.8933 178.859 16.007 180.672 17.9273C182.485 19.835 182.367 22.8166 180.385 24.5727C174.921 29.4493 170.395 35.4882 167.292 42.0325C166.483 43.7381 164.749 44.7614 162.923 44.774Z" fill="black"/>
<path d="M146.57 42.0578C144.053 42.083 141.901 40.2132 141.692 37.737C140.766 26.6572 140.727 15.4258 141.601 4.34602C141.809 1.7561 144.144 -0.176866 146.804 0.0126405C149.478 0.214781 151.473 2.46359 151.277 5.05351C150.443 15.6406 150.482 26.3793 151.369 36.9664C151.59 39.5563 149.595 41.8304 146.922 42.0325C146.804 42.0325 146.687 42.0451 146.57 42.0451V42.0578Z" fill="black"/>
<path d="M129.851 44.9762C127.673 45.0015 125.665 43.5739 125.104 41.4388C123.043 33.6058 119.144 26.1645 113.836 19.9361C112.128 17.9273 112.428 14.971 114.488 13.316C116.562 11.661 119.614 11.9516 121.322 13.9477C127.53 21.25 132.094 29.9547 134.494 39.1141C135.159 41.6283 133.581 44.1929 130.985 44.8372C130.594 44.9383 130.216 44.9762 129.825 44.9888L129.851 44.9762Z" fill="black"/>
</g>
</g>
<path d="M379.045 147.144L419.52 55.0319C423.451 46.2048 424.882 39.0196 425.244 34.35C425.349 32.9943 426.419 31.9097 427.775 31.7892L440.789 30.5691C441.979 30.4637 443.109 31.1114 443.606 32.211L492.788 141.571C500.244 158.005 502.94 165.566 513.153 168.715C514.313 169.076 515.127 170.161 515.127 171.381V173.655C515.127 175.207 513.876 176.457 512.325 176.457H455.822C454.271 176.457 453.021 175.207 453.021 173.655V171.321C453.021 170.01 453.955 168.88 455.235 168.594C466.954 166.003 466.728 160.068 462.556 150.413L459.528 143.438C456.741 136.931 451.62 135.997 445.805 135.997H407.665C401.384 135.997 396.97 137.157 394.409 143.212L391.849 149.027C387.224 159.827 391.999 166.033 405.195 168.639C406.505 168.895 407.439 170.055 407.439 171.381V173.685C407.439 175.237 406.189 176.487 404.637 176.487H358.589C357.037 176.487 355.787 175.237 355.787 173.685V171.245C355.787 169.98 356.631 168.88 357.851 168.549C369.344 165.476 373.984 158.622 379.045 147.174V147.144ZM407.665 125.98H445.579C448.14 125.98 450.46 123.886 448.833 120.166L428.603 75.051C426.75 71.0894 425.816 71.0894 424.19 75.051L404.185 120.874C402.785 124.127 405.345 125.995 407.68 125.995L407.665 125.98Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M527.675 156.227V50.1663C527.675 40.5107 524.075 37.3023 515.685 37.4378C514.103 37.468 512.777 36.2328 512.777 34.6361V32.708C512.777 31.3523 513.741 30.2074 515.067 29.9514L545.344 24.3328C552.093 23.1729 555.572 26.2006 555.572 32.4669V156.212C555.572 162.825 558.329 166.184 563.179 165.883C564.836 165.777 566.267 166.997 566.267 168.669V168.745C566.267 169.227 566.162 169.709 565.921 170.115C562.682 175.689 555.316 177.843 548.598 177.843C536.502 177.843 527.66 171.094 527.66 156.212L527.675 156.227Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M581.748 156.227V50.1663C581.748 40.5107 578.148 37.3023 569.758 37.4378C568.176 37.468 566.851 36.2328 566.851 34.6361V32.708C566.851 31.3523 567.815 30.2074 569.14 29.9514L599.417 24.3328C606.166 23.1729 609.645 26.2006 609.645 32.4669V156.212C609.645 162.825 612.402 166.184 617.252 165.883C618.909 165.777 620.34 166.997 620.34 168.669V168.745C620.34 169.227 620.235 169.709 619.994 170.115C616.755 175.689 609.389 177.843 602.671 177.843C590.575 177.843 581.733 171.094 581.733 156.212L581.748 156.227Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M688.158 155.052V53.4201C688.158 45.2558 683.383 41.4599 674.405 39.818C673.079 39.577 672.115 38.4171 672.115 37.0765V34.817C672.115 33.2655 673.365 32.0152 674.917 32.0152H733.046C734.598 32.0152 735.848 33.2655 735.848 34.817V37.0765C735.848 38.4171 734.884 39.577 733.558 39.818C724.566 41.4448 719.806 45.2408 719.806 53.4201V92.9613C719.806 98.3087 723.993 100.644 729.808 100.644H770.283C776.097 100.644 780.285 98.3238 780.285 92.9613V53.4201C780.285 45.2558 775.51 41.4599 766.532 39.818C765.206 39.577 764.242 38.4171 764.242 37.0765V34.817C764.242 33.2655 765.493 32.0152 767.044 32.0152H825.173C826.725 32.0152 827.975 33.2655 827.975 34.817V37.0765C827.975 38.4171 827.011 39.577 825.686 39.818C816.708 41.4448 811.933 45.2408 811.933 53.4201V155.067C811.933 163.232 816.708 167.027 825.686 168.669C827.011 168.91 827.975 170.07 827.975 171.411V173.67C827.975 175.222 826.725 176.472 825.173 176.472H767.044C765.493 176.472 764.242 175.222 764.242 173.67V171.411C764.242 170.07 765.206 168.91 766.532 168.669C775.525 167.043 780.285 163.247 780.285 155.067V117.62C780.285 112.272 776.097 109.938 770.524 109.938H729.582C723.993 109.938 719.821 112.257 719.821 117.62V155.067C719.821 163.232 724.596 167.027 733.573 168.669C734.899 168.91 735.863 170.07 735.863 171.411V173.67C735.863 175.222 734.613 176.472 733.061 176.472H674.932C673.381 176.472 672.13 175.222 672.13 173.67V171.411C672.13 170.07 673.094 168.91 674.42 168.669C683.398 167.043 688.173 163.247 688.173 155.067L688.158 155.052Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M834.929 152.265C834.929 133.421 850.745 122.033 896.101 115.511C902.608 114.577 904.476 112.95 904.476 106.909V103.189C904.476 86.4384 899.128 77.8373 885.873 77.8373C859.361 77.8373 879.591 108.777 855.4 108.777C846.091 108.777 841.677 102.963 841.677 95.5216C841.677 77.6114 867.029 69.4772 892.621 69.4772C920.066 69.4772 932.388 79.4792 932.388 111.805V156.227C932.388 162.855 934.346 166.379 938.941 165.973C940.553 165.837 941.923 167.148 941.923 168.759C941.923 169.226 941.818 169.708 941.577 170.115C938.414 175.688 931.891 177.843 924.947 177.843C911.917 177.843 905.877 170.16 904.943 161.8C904.717 159.013 902.849 158.546 900.755 161.107C894.248 169.241 882.152 177.858 865.402 177.858C849.585 177.858 834.929 170.416 834.929 152.28V152.265ZM879.817 162.267C894.007 162.267 904.476 151.331 904.476 136.675V130.634C904.476 125.287 902.623 122.952 895.167 123.66C869.348 125.98 863.534 137.608 863.534 146.692C863.534 155.067 868.414 162.282 879.817 162.282V162.267Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M1038.21 156.227V114.125C1038.21 98.0829 1030.3 85.746 1015.89 85.746C1001.47 85.746 992.856 98.0678 992.856 114.125V154.359C992.856 164.211 996.2 167.751 1005.07 168.971C1006.46 169.167 1007.51 170.341 1007.51 171.742V173.655C1007.51 175.207 1006.26 176.457 1004.71 176.457H952.863C951.311 176.457 950.061 175.207 950.061 173.655V171.757C950.061 170.357 951.115 169.182 952.501 168.986C961.584 167.766 964.944 164.226 964.944 154.359V95.7481C964.944 86.0925 961.343 82.884 952.953 83.0196C951.372 83.0497 950.046 81.8145 950.046 80.2178V78.2897C950.046 76.934 951.01 75.7892 952.336 75.5331L982.613 69.9145C989.361 68.7546 992.841 71.7824 992.841 77.1298C992.841 85.505 996.561 87.5988 999.589 84.1041C1004.47 78.2897 1013.31 68.9806 1031.69 68.9806C1048.91 68.9806 1066.11 77.3558 1066.11 108.748V156.197C1066.11 162.81 1068.87 166.169 1073.72 165.868C1075.37 165.762 1076.8 166.982 1076.8 168.654V168.73C1076.8 169.212 1076.7 169.694 1076.46 170.1C1073.22 175.674 1065.85 177.828 1059.13 177.828C1047.04 177.828 1038.2 171.08 1038.2 156.197L1038.21 156.227Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M1082.63 125.061C1082.63 88.7735 1107.52 69.4775 1133.11 69.4775C1157.06 69.4775 1165.21 86.6948 1165.21 67.6096V50.1663C1165.21 40.5107 1161.61 37.3023 1153.22 37.4378C1151.64 37.468 1150.31 36.2328 1150.31 34.6361V32.708C1150.31 31.3523 1151.27 30.2074 1152.6 29.9514L1182.88 24.3328C1189.63 23.1729 1193.1 26.2006 1193.1 32.4669V156.212C1193.1 162.825 1195.86 166.184 1200.71 165.883C1202.37 165.777 1203.8 166.997 1203.8 168.669V168.745C1203.8 169.227 1203.69 169.709 1203.45 170.115C1200.21 175.689 1192.86 177.843 1186.36 177.843C1178.67 177.843 1170.77 174.815 1168.22 167.841C1166.82 164.12 1164.73 162.493 1162.18 162.493C1156.59 162.493 1148.46 177.843 1127.29 177.843C1104.04 177.843 1082.63 158.532 1082.63 125.046V125.061ZM1142.64 161.574C1152.64 161.574 1165.21 155.534 1165.21 135.289V117.62C1165.21 89.2405 1148.92 78.0786 1135.89 78.0786C1120.77 78.0786 1111.94 93.4281 1111.94 114.592C1111.94 139.718 1124.27 161.574 1142.64 161.574Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<path d="M1219.29 167.826C1218.9 167.539 1218.6 167.163 1218.39 166.726C1212.86 154.781 1212.35 141.103 1221.02 141.103C1224.28 141.103 1226.37 142.73 1227.53 146.225C1233.12 162.975 1242.88 169.483 1256.6 169.483C1272.19 169.483 1276.61 161.574 1276.61 154.133C1276.61 143.905 1267.07 140.636 1252.41 136.223C1233.57 130.408 1215.66 121.34 1215.66 101.095C1215.66 80.8501 1234.5 69.4623 1258.45 69.4623C1272.06 69.4623 1284.77 73.213 1292.72 78.7865C1293.13 79.0727 1293.46 79.4643 1293.67 79.9162C1299.19 91.8463 1299.68 105.268 1291.02 105.268C1287.77 105.268 1285.67 103.641 1284.51 100.146C1278.93 83.3958 1268.92 77.3554 1257.07 77.3554C1245.91 77.3554 1240.79 82.9439 1240.79 91.3041C1240.79 101.773 1249.63 106.895 1260.79 110.374C1277.54 115.496 1302.65 121.777 1302.65 145.035C1302.65 166.666 1280.33 177.828 1254.98 177.828C1240 177.828 1227.23 173.625 1219.28 167.795L1219.29 167.826Z" fill="black" stroke="black" stroke-width="0.393644" stroke-miterlimit="10"/>
<defs>
<clipPath id="clip0_14027_2415">
<rect width="294" height="196" fill="white"/>
</clipPath>
<clipPath id="clip1_14027_2415">
<rect width="294" height="196" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

2091
docs/openapi.json Normal file

File diff suppressed because it is too large Load Diff

BIN
docs/static/img/backend_architecture.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

View File

@@ -0,0 +1,201 @@
@startuml openhands
!pragma useIntermediatePackages false
class openhands.action.agent.AgentEchoAction {
content: str
runnable: bool
action: str
}
class openhands.action.agent.AgentFinishAction {
runnable: bool
action: str
}
class openhands.observation.AgentMessageObservation {
role: str
observation: str
}
class openhands.action.agent.AgentSummarizeAction {
summary: str
action: str
}
class openhands.action.agent.AgentThinkAction {
thought: str
runnable: bool
action: str
}
class openhands.action.base.ExecutableAction {
}
class openhands.action.base.NotExecutableAction {
}
class openhands.observation.Observation {
content: str
}
class openhands.action.base.Action {
}
class openhands.action.base.NullAction {
action: str
}
class openhands.action.bash.CmdRunAction {
command: str
action: str
}
class openhands.action.browse.BrowseURLAction {
url: str
action: str
}
class openhands.observation.BrowserOutputObservation {
url: str
status_code: int
error: bool
observation: str
}
class openhands.action.fileop.FileReadAction {
path: str
action: str
}
class openhands.observation.FileReadObservation {
path: str
observation: str
}
class openhands.action.fileop.FileWriteAction {
path: str
contents: str
action: str
}
class openhands.observation.FileWriteObservation {
path: str
observation: str
}
class openhands.action.tasks.AddTaskAction {
parent: str
goal: str
subtasks: list
action: str
}
class openhands.action.tasks.ModifyTaskAction {
id: str
state: str
action: str
}
abstract class openhands.agent.Agent {
_registry: Dict[str, Type[Agent]] {static}
llm: LLM
_complete: None
}
class openhands.llm.llm.LLM {
model: None
api_key: None
base_url: None
_debug_dir: None
_debug_idx: None
_debug_id: None
_completion: None
}
class openhands.controller.agent_controller.AgentController {
agent: Agent
max_iterations: int
workdir: str
command_manager: CommandManager
state: State
plan: Plan
callbacks: List[Callable]
}
class openhands.observation.AgentErrorObservation {
observation: str
}
class openhands.controller.command_manager.CommandManager {
directory: None
shell: None
}
class openhands.observation.NullObservation {
observation: str
}
class openhands.plan.Plan {
main_goal: str {static}
task: Task {static}
main_goal: str
task: None
}
class openhands.state.State {
plan: Plan
iteration: int
history: List[Tuple[Action, Observation]]
updated_info: List[Tuple[Action, Observation]]
}
class openhands.observation.CmdOutputObservation {
command: str
exit_code: int
observation: str
}
class openhands.sandbox.sandbox.DockerInteractive {
instance_id: None
instance_id: None
workspace_dir: None
workspace_dir: None
workspace_dir: None
timeout: int
base_container_image: None
container_name: None
}
class openhands.observation.UserMessageObservation {
role: str
observation: str
}
class openhands.plan.Task {
id: str {static}
goal: str {static}
parent: Task | None {static}
subtasks: List[Task] {static}
id: None
id: None
parent: None
goal: str
subtasks: None
}
class openhands.server.session.Session {
websocket: None
controller: Optional[AgentController]
agent: Optional[Agent]
agent_task: None
}
openhands.action.base.ExecutableAction <|-- openhands.action.agent.AgentEchoAction
openhands.action.base.NotExecutableAction <|-- openhands.action.agent.AgentFinishAction
openhands.observation.Observation <|-- openhands.observation.AgentMessageObservation
openhands.action.base.NotExecutableAction <|-- openhands.action.agent.AgentSummarizeAction
openhands.action.base.NotExecutableAction <|-- openhands.action.agent.AgentThinkAction
openhands.action.base.Action <|-- openhands.action.base.ExecutableAction
openhands.action.base.Action <|-- openhands.action.base.NotExecutableAction
openhands.action.base.NotExecutableAction <|-- openhands.action.base.NullAction
openhands.action.base.ExecutableAction <|-- openhands.action.bash.CmdRunAction
openhands.action.base.ExecutableAction <|-- openhands.action.browse.BrowseURLAction
openhands.observation.Observation <|-- openhands.observation.BrowserOutputObservation
openhands.action.base.ExecutableAction <|-- openhands.action.fileop.FileReadAction
openhands.observation.Observation <|-- openhands.observation.FileReadObservation
openhands.action.base.ExecutableAction <|-- openhands.action.fileop.FileWriteAction
openhands.observation.Observation <|-- openhands.observation.FileWriteObservation
openhands.action.base.NotExecutableAction <|-- openhands.action.tasks.AddTaskAction
openhands.action.base.NotExecutableAction <|-- openhands.action.tasks.ModifyTaskAction
openhands.agent.Agent *-- openhands.agent.Agent
openhands.agent.Agent *-- openhands.llm.llm.LLM
openhands.controller.agent_controller.AgentController *-- openhands.agent.Agent
openhands.observation.Observation <|-- openhands.observation.AgentErrorObservation
openhands.observation.Observation <|-- openhands.observation.NullObservation
openhands.plan.Plan *-- openhands.plan.Task
openhands.state.State *-- openhands.plan.Plan
openhands.state.State *-- openhands.observation.CmdOutputObservation
openhands.state.State *-- openhands.action.base.Action
openhands.state.State *-- openhands.observation.Observation
openhands.observation.Observation <|-- openhands.observation.CmdOutputObservation
openhands.observation.Observation <|-- openhands.observation.UserMessageObservation
openhands.plan.Task *-- openhands.plan.Task
openhands.server.session.Session *-- openhands.controller.agent_controller.AgentController
openhands.server.session.Session *-- openhands.agent.Agent
openhands.controller.agent_controller.AgentController -> openhands.state.State
openhands.controller.agent_controller.AgentController -> openhands.plan.Plan
openhands.controller.agent_controller.AgentController -> openhands.controller.command_manager.CommandManager
openhands.controller.command_manager.CommandManager -> openhands.sandbox.sandbox.DockerInteractive
footer Based on f3fda42; Generated by //py2puml//
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 113 KiB

BIN
docs/static/img/connect-repo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/static/img/logo-square.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/static/img/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/static/img/oh-features.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
docs/static/img/results.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

BIN
docs/static/img/screenshot.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 KiB

BIN
docs/static/img/system_architecture.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,67 @@
@startuml "System Architecture"
node frontend as frontend{
component App
package components{
component Terminal
component ChatInterface
component BannerSettings
}
package services{
component chatService
component settingsService
chatService -[hidden]u-> settingsService
}
package socket
App -> Terminal
App -> ChatInterface
App -> BannerSettings
ChatInterface -> chatService
BannerSettings -> settingsService
Terminal -> socket
chatService -d-> socket
settingsService -d-> socket
services -[hidden]d-> socket
Terminal -[hidden]u-> ChatInterface
ChatInterface -[hidden]u-> BannerSettings
interface "HTTP (:3001)" as HTTP
HTTP - App
}
node backend{
package server as serverpackage{
component Server
'defined in server/server.py, port is defined at startup with uvicorn
interface "Client WS\n(:3000/ws)" as client_socket
client_socket - Server
}
node AgentController{
}
Server -d-> AgentController
}
socket -d-> client_socket: connects to \n VITE_TERMINAL_WS_URL
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
docs/static/img/teaser.mp4 vendored Normal file

Binary file not shown.

30
docs/usage/about.mdx Normal file
View File

@@ -0,0 +1,30 @@
---
title: About OpenHands
---
## Research Strategy
Achieving full replication of production-grade applications with LLMs is a complex endeavor. Our strategy involves:
- **Core Technical Research:** Focusing on foundational research to understand and improve the technical aspects of code generation and handling.
- **Task Planning:** Developing capabilities for bug detection, codebase management, and optimization.
- **Evaluation:** Establishing comprehensive evaluation metrics to better understand and improve our agents.
## Default Agent
Our default Agent is currently the [CodeActAgent](./agents), which is capable of generating code and handling files.
## Built With
OpenHands is built using a combination of powerful frameworks and libraries, providing a robust foundation for its
development. Here are the key technologies used in the project:
![FastAPI](https://img.shields.io/badge/FastAPI-black?style=for-the-badge) ![uvicorn](https://img.shields.io/badge/uvicorn-black?style=for-the-badge) ![LiteLLM](https://img.shields.io/badge/LiteLLM-black?style=for-the-badge) ![Docker](https://img.shields.io/badge/Docker-black?style=for-the-badge) ![Ruff](https://img.shields.io/badge/Ruff-black?style=for-the-badge) ![MyPy](https://img.shields.io/badge/MyPy-black?style=for-the-badge) ![LlamaIndex](https://img.shields.io/badge/LlamaIndex-black?style=for-the-badge) ![React](https://img.shields.io/badge/React-black?style=for-the-badge)
Please note that the selection of these technologies is in progress, and additional technologies may be added or
existing ones may be removed as the project evolves. We strive to adopt the most suitable and efficient tools to
enhance the capabilities of OpenHands.
## License
Distributed under MIT [License](https://github.com/All-Hands-AI/OpenHands/blob/main/LICENSE).

26
docs/usage/agents.mdx Normal file
View File

@@ -0,0 +1,26 @@
---
title: Main Agent and Capabilities
---
## CodeActAgent
### Description
This agent implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.01030), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents **act**ions into a
unified **code** action space for both _simplicity_ and _performance_.
The conceptual idea is illustrated below. At each turn, the agent can:
1. **Converse**: Communicate with humans in natural language to ask for clarification, confirmation, etc.
2. **CodeAct**: Choose to perform the task by executing code
- Execute any valid Linux `bash` command
- Execute any valid `Python` code with [an interactive Python interpreter](https://ipython.org/). This is simulated through `bash` command, see plugin system below for more details.
![image](https://github.com/All-Hands-AI/OpenHands/assets/38853559/92b622e3-72ad-4a61-8f41-8c040b6d5fb3)
### Demo
https://github.com/All-Hands-AI/OpenHands/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac
_Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)_.

View File

@@ -0,0 +1,56 @@
---
title: Backend Architecture
---
<div style={{ textAlign: 'center' }}>
<img src="https://github.com/All-Hands-AI/OpenHands/assets/16201837/97d747e3-29d8-4ccb-8d34-6ad1adb17f38" alt="OpenHands System Architecture Diagram Jul 4 2024" />
<p><em>OpenHands System Architecture Diagram (July 4, 2024)</em></p>
</div>
This is a high-level overview of the system architecture. The system is divided into two main components: the frontend and the backend. The frontend is responsible for handling user interactions and displaying the results. The backend is responsible for handling the business logic and executing the agents.
# Frontend architecture
![system_architecture.svg](/static/img/system_architecture.svg)
This Overview is simplified to show the main components and their interactions. For a more detailed view of the backend architecture, see the Backend Architecture section below.
# Backend Architecture
_**Disclaimer**: The backend architecture is a work in progress and is subject to change. The following diagram shows the current architecture of the backend based on the commit that is shown in the footer of the diagram._
![backend_architecture.svg](/static/img/backend_architecture.svg)
<details>
<summary>Updating this Diagram</summary>
<div>
The generation of the backend architecture diagram is partially automated.
The diagram is generated from the type hints in the code using the py2puml
tool. The diagram is then manually reviewed, adjusted and exported to PNG
and SVG.
## Prerequisites
- Running python environment in which openhands is executable
(according to the instructions in the README.md file in the root of the repository)
- [py2puml](https://github.com/lucsorel/py2puml) installed
## Steps
1. Autogenerate the diagram by running the following command from the root of the repository:
`py2puml openhands openhands > docs/architecture/backend_architecture.puml`
2. Open the generated file in a PlantUML editor, e.g. Visual Studio Code with the PlantUML extension or [PlantText](https://www.planttext.com/)
3. Review the generated PUML and make all necessary adjustments to the diagram (add missing parts, fix mistakes, improve positioning).
_py2puml creates the diagram based on the type hints in the code, so missing or incorrect type hints may result in an incomplete or incorrect diagram._
4. Review the diff between the new and the previous diagram and manually check if the changes are correct.
_Make sure not to remove parts that were manually added to the diagram in the past and are still relevant._
5. Add the commit hash of the commit that was used to generate the diagram to the diagram footer.
6. Export the diagram as PNG and SVG files and replace the existing diagrams in the `docs/architecture` directory. This can be done with (e.g. [PlantText](https://www.planttext.com/))
</div>
</details>

View File

@@ -0,0 +1,136 @@
---
title: Runtime Architecture
---
The OpenHands Docker Runtime is the core component that enables secure and flexible execution of AI agent's action.
It creates a sandboxed environment using Docker, where arbitrary code can be run safely without risking the host system.
## Why do we need a sandboxed runtime?
OpenHands needs to execute arbitrary code in a secure, isolated environment for several reasons:
1. Security: Executing untrusted code can pose significant risks to the host system. A sandboxed environment prevents malicious code from accessing or modifying the host system's resources
2. Consistency: A sandboxed environment ensures that code execution is consistent across different machines and setups, eliminating "it works on my machine" issues
3. Resource Control: Sandboxing allows for better control over resource allocation and usage, preventing runaway processes from affecting the host system
4. Isolation: Different projects or users can work in isolated environments without interfering with each other or the host system
5. Reproducibility: Sandboxed environments make it easier to reproduce bugs and issues, as the execution environment is consistent and controllable
## How does the Runtime work?
The OpenHands Runtime system uses a client-server architecture implemented with Docker containers. Here's an overview of how it works:
```mermaid
graph TD
A[User-provided Custom Docker Image] --> B[OpenHands Backend]
B -->|Builds| C[OH Runtime Image]
C -->|Launches| D[Action Executor]
D -->|Initializes| E[Browser]
D -->|Initializes| F[Bash Shell]
D -->|Initializes| G[Plugins]
G -->|Initializes| L[Jupyter Server]
B -->|Spawn| H[Agent]
B -->|Spawn| I[EventStream]
I <--->|Execute Action to
Get Observation
via REST API
| D
H -->|Generate Action| I
I -->|Obtain Observation| H
subgraph "Docker Container"
D
E
F
G
L
end
```
1. User Input: The user provides a custom base Docker image
2. Image Building: OpenHands builds a new Docker image (the "OH runtime image") based on the user-provided image. This new image includes OpenHands-specific code, primarily the "runtime client"
3. Container Launch: When OpenHands starts, it launches a Docker container using the OH runtime image
4. Action Execution Server Initialization: The action execution server initializes an `ActionExecutor` inside the container, setting up necessary components like a bash shell and loading any specified plugins
5. Communication: The OpenHands backend (`openhands/runtime/impl/eventstream/eventstream_runtime.py`) communicates with the action execution server over RESTful API, sending actions and receiving observations
6. Action Execution: The runtime client receives actions from the backend, executes them in the sandboxed environment, and sends back observations
7. Observation Return: The action execution server sends execution results back to the OpenHands backend as observations
The role of the client:
- It acts as an intermediary between the OpenHands backend and the sandboxed environment
- It executes various types of actions (shell commands, file operations, Python code, etc.) safely within the container
- It manages the state of the sandboxed environment, including the current working directory and loaded plugins
- It formats and returns observations to the backend, ensuring a consistent interface for processing results
## How OpenHands builds and maintains OH Runtime images
OpenHands' approach to building and managing runtime images ensures efficiency, consistency, and flexibility in creating and maintaining Docker images for both production and development environments.
Check out the [relevant code](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/utils/runtime_build.py) if you are interested in more details.
### Image Tagging System
OpenHands uses a three-tag system for its runtime images to balance reproducibility with flexibility.
Tags may be in one of 2 formats:
- **Versioned Tag**: `oh_v{openhands_version}_{base_image}` (e.g.: `oh_v0.9.9_nikolaik_s_python-nodejs_t_python3.12-nodejs22`)
- **Lock Tag**: `oh_v{openhands_version}_{16_digit_lock_hash}` (e.g.: `oh_v0.9.9_1234567890abcdef`)
- **Source Tag**: `oh_v{openhands_version}_{16_digit_lock_hash}_{16_digit_source_hash}`
(e.g.: `oh_v0.9.9_1234567890abcdef_1234567890abcdef`)
#### Source Tag - Most Specific
This is the first 16 digits of the MD5 of the directory hash for the source directory. This gives a hash
for only the openhands source
#### Lock Tag
This hash is built from the first 16 digits of the MD5 of:
- The name of the base image upon which the image was built (e.g.: `nikolaik/python-nodejs:python3.12-nodejs22`)
- The content of the `pyproject.toml` included in the image.
- The content of the `poetry.lock` included in the image.
This effectively gives a hash for the dependencies of Openhands independent of the source code.
#### Versioned Tag - Most Generic
This tag is a concatenation of openhands version and the base image name (transformed to fit in tag standard).
#### Build Process
When generating an image...
- **No re-build**: OpenHands first checks whether an image with the same **most specific source tag** exists. If there is such an image,
no build is performed - the existing image is used.
- **Fastest re-build**: OpenHands next checks whether an image with the **generic lock tag** exists. If there is such an image,
OpenHands builds a new image based upon it, bypassing all installation steps (like `poetry install` and
`apt-get`) except a final operation to copy the current source code. The new image is tagged with a
**source** tag only.
- **Ok-ish re-build**: If neither a **source** nor **lock** tag exists, an image will be built based upon the **versioned** tag image.
In versioned tag image, most dependencies should already been installed hence saving time.
- **Slowest re-build**: If all of the three tags don't exists, a brand new image is built based upon the base
image (Which is a slower operation). This new image is tagged with all the **source**, **lock**, and **versioned** tags.
This tagging approach allows OpenHands to efficiently manage both development and production environments.
1. Identical source code and Dockerfile always produce the same image (via hash-based tags)
2. The system can quickly rebuild images when minor changes occur (by leveraging recent compatible images)
3. The **lock** tag (e.g., `runtime:oh_v0.9.3_1234567890abcdef`) always points to the latest build for a particular base image, dependency, and OpenHands version combination
## Runtime Plugin System
The OpenHands Runtime supports a plugin system that allows for extending functionality and customizing the runtime environment. Plugins are initialized when the runtime client starts up.
Check [an example of Jupyter plugin here](https://github.com/All-Hands-AI/OpenHands/blob/ecf4aed28b0cf7c18d4d8ff554883ba182fc6bdd/openhands/runtime/plugins/jupyter/__init__.py#L21-L55) if you want to implement your own plugin.
*More details about the Plugin system are still under construction - contributions are welcomed!*
Key aspects of the plugin system:
1. Plugin Definition: Plugins are defined as Python classes that inherit from a base `Plugin` class
2. Plugin Registration: Available plugins are registered in an `ALL_PLUGINS` dictionary
3. Plugin Specification: Plugins are associated with `Agent.sandbox_plugins: list[PluginRequirement]`. Users can specify which plugins to load when initializing the runtime
4. Initialization: Plugins are initialized asynchronously when the runtime client starts
5. Usage: The runtime client can use initialized plugins to extend its capabilities (e.g., the JupyterPlugin for running IPython cells)

Some files were not shown because too many files have changed in this diff Show More