mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68ca0b6a9b |
@@ -0,0 +1,53 @@
|
|||||||
|
# Workflow that uses the DummyAgent to run a simple task
|
||||||
|
name: Run E2E test with dummy agent
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
test:
|
||||||
|
runs-on: blacksmith-4vcpu-ubuntu-2204
|
||||||
|
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: '3.12'
|
||||||
|
cache: 'poetry'
|
||||||
|
- name: Install Python dependencies using Poetry
|
||||||
|
run: poetry install --without evaluation
|
||||||
|
- name: Build Environment
|
||||||
|
run: make build
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
SANDBOX_FORCE_REBUILD_RUNTIME=True poetry run python3 openhands/core/main.py -t "do a flip" -d ./workspace/ -c DummyAgent
|
||||||
|
- name: Check exit code
|
||||||
|
run: |
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Test failed"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Test passed"
|
||||||
|
fi
|
||||||
@@ -24,7 +24,7 @@ on:
|
|||||||
LLM_MODEL:
|
LLM_MODEL:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: "anthropic/claude-3-7-sonnet-20250219"
|
default: "anthropic/claude-3-5-sonnet-20241022"
|
||||||
LLM_API_VERSION:
|
LLM_API_VERSION:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
@@ -179,7 +179,7 @@ jobs:
|
|||||||
|
|
||||||
echo "MAX_ITERATIONS=${{ inputs.max_iterations || 50 }}" >> $GITHUB_ENV
|
echo "MAX_ITERATIONS=${{ inputs.max_iterations || 50 }}" >> $GITHUB_ENV
|
||||||
echo "SANDBOX_ENV_GITHUB_TOKEN=${{ secrets.PAT_TOKEN || github.token }}" >> $GITHUB_ENV
|
echo "SANDBOX_ENV_GITHUB_TOKEN=${{ secrets.PAT_TOKEN || github.token }}" >> $GITHUB_ENV
|
||||||
echo "SANDBOX_BASE_CONTAINER_IMAGE=${{ inputs.base_container_image }}" >> $GITHUB_ENV
|
echo "SANDBOX_ENV_BASE_CONTAINER_IMAGE=${{ inputs.base_container_image }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
# Set branch variables
|
# Set branch variables
|
||||||
echo "TARGET_BRANCH=${{ inputs.target_branch || 'main' }}" >> $GITHUB_ENV
|
echo "TARGET_BRANCH=${{ inputs.target_branch || 'main' }}" >> $GITHUB_ENV
|
||||||
|
|||||||
@@ -54,20 +54,3 @@ Frontend:
|
|||||||
## Template for Github Pull Request
|
## Template for Github Pull Request
|
||||||
|
|
||||||
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
|
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
|
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ poetry run pytest ./tests/unit/test_*.py
|
|||||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker container image by
|
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.
|
setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||||
|
|
||||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.34-nikolaik`
|
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.32-nikolaik`
|
||||||
|
|
||||||
## Develop inside Docker container
|
## Develop inside Docker container
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ ifeq ($(INSTALL_DOCKER),)
|
|||||||
@$(MAKE) -s check-docker
|
@$(MAKE) -s check-docker
|
||||||
endif
|
endif
|
||||||
@$(MAKE) -s check-poetry
|
@$(MAKE) -s check-poetry
|
||||||
@$(MAKE) -s check-tmux
|
|
||||||
@echo "$(GREEN)Dependencies checked successfully.$(RESET)"
|
@echo "$(GREEN)Dependencies checked successfully.$(RESET)"
|
||||||
|
|
||||||
check-system:
|
check-system:
|
||||||
@@ -102,18 +101,6 @@ check-docker:
|
|||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
check-tmux:
|
|
||||||
@echo "$(YELLOW)Checking tmux installation...$(RESET)"
|
|
||||||
@if command -v tmux > /dev/null; then \
|
|
||||||
echo "$(BLUE)$(shell tmux -V) is already installed.$(RESET)"; \
|
|
||||||
else \
|
|
||||||
echo "$(YELLOW)╔════════════════════════════════════════════════════════════════════════════╗$(RESET)"; \
|
|
||||||
echo "$(YELLOW)║ OPTIONAL: tmux is not installed. ║$(RESET)"; \
|
|
||||||
echo "$(YELLOW)║ Some advanced terminal features may not work without tmux. ║$(RESET)"; \
|
|
||||||
echo "$(YELLOW)║ You can install it if needed, but it's not required for development. ║$(RESET)"; \
|
|
||||||
echo "$(YELLOW)╚════════════════════════════════════════════════════════════════════════════╝$(RESET)"; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
check-poetry:
|
check-poetry:
|
||||||
@echo "$(YELLOW)Checking Poetry installation...$(RESET)"
|
@echo "$(YELLOW)Checking Poetry installation...$(RESET)"
|
||||||
@if command -v poetry > /dev/null; then \
|
@if command -v poetry > /dev/null; then \
|
||||||
@@ -188,7 +175,7 @@ install-pre-commit-hooks:
|
|||||||
|
|
||||||
lint-backend:
|
lint-backend:
|
||||||
@echo "$(YELLOW)Running linters...$(RESET)"
|
@echo "$(YELLOW)Running linters...$(RESET)"
|
||||||
@poetry run pre-commit run --files openhands/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config $(PRE_COMMIT_CONFIG_PATH)
|
@poetry run pre-commit run --files openhands/**/* agenthub/**/* evaluation/**/* --show-diff-on-failure --config $(PRE_COMMIT_CONFIG_PATH)
|
||||||
|
|
||||||
lint-frontend:
|
lint-frontend:
|
||||||
@echo "$(YELLOW)Running linters for frontend...$(RESET)"
|
@echo "$(YELLOW)Running linters for frontend...$(RESET)"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<a href="https://docs.all-hands.dev/modules/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://docs.all-hands.dev/modules/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://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>
|
<a href="https://huggingface.co/spaces/OpenHands/evaluation"><img src="https://img.shields.io/badge/Benchmark%20score-000?logoColor=FFE165&logo=huggingface&style=for-the-badge" alt="Evaluation Benchmark Score"></a>
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -52,17 +52,17 @@ system requirements and more information.
|
|||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik
|
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik
|
||||||
|
|
||||||
docker run -it --rm --pull=always \
|
docker run -it --rm --pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e LOG_ALL_EVENTS=true \
|
-e LOG_ALL_EVENTS=true \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app \
|
--name openhands-app \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34
|
docker.all-hands.dev/all-hands-ai/openhands:0.32
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||||
|
|||||||
@@ -221,22 +221,9 @@ enable_browsing = true
|
|||||||
# Whether the LLM draft editor is enabled
|
# Whether the LLM draft editor is enabled
|
||||||
enable_llm_editor = false
|
enable_llm_editor = false
|
||||||
|
|
||||||
# Whether the standard editor tool (str_replace_editor) is enabled
|
|
||||||
# Only has an effect if enable_llm_editor is False
|
|
||||||
enable_editor = true
|
|
||||||
|
|
||||||
# Whether the IPython tool is enabled
|
# Whether the IPython tool is enabled
|
||||||
enable_jupyter = true
|
enable_jupyter = true
|
||||||
|
|
||||||
# Whether the command tool is enabled
|
|
||||||
enable_cmd = true
|
|
||||||
|
|
||||||
# Whether the think tool is enabled
|
|
||||||
enable_think = true
|
|
||||||
|
|
||||||
# Whether the finish tool is enabled
|
|
||||||
enable_finish = true
|
|
||||||
|
|
||||||
# LLM config group to use
|
# LLM config group to use
|
||||||
#llm_config = 'your-llm-config-group'
|
#llm_config = 'your-llm-config-group'
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ RUN add-apt-repository ppa:deadsnakes/ppa \
|
|||||||
&& apt-get install -y python3.12 python3.12-venv python3.12-dev python3-pip \
|
&& apt-get install -y python3.12 python3.12-venv python3.12-dev python3-pip \
|
||||||
&& ln -s /usr/bin/python3.12 /usr/bin/python
|
&& ln -s /usr/bin/python3.12 /usr/bin/python
|
||||||
|
|
||||||
# NodeJS >= 22.x
|
# NodeJS >= 18.17.1
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
|
||||||
&& apt-get install -y nodejs
|
&& apt-get install -y nodejs
|
||||||
|
|
||||||
# Poetry >= 1.8
|
# Poetry >= 1.8
|
||||||
@@ -108,7 +108,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
# cache build dependencies
|
# cache build dependencies
|
||||||
RUN \
|
RUN \
|
||||||
--mount=type=bind,source=./,target=/app/,rw \
|
--mount=type=bind,source=./,target=/app/ \
|
||||||
<<EOF
|
<<EOF
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
make -s clean
|
make -s clean
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ services:
|
|||||||
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
|
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
|
||||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||||
#
|
#
|
||||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.34-nikolaik}
|
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.32-nikolaik}
|
||||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ services:
|
|||||||
image: openhands:latest
|
image: openhands:latest
|
||||||
container_name: openhands-app-${DATE:-}
|
container_name: openhands-app-${DATE:-}
|
||||||
environment:
|
environment:
|
||||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik}
|
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.32-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
|
#- 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}
|
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
# Production
|
# Production
|
||||||
/build
|
/build
|
||||||
/static/swagger-ui
|
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
.docusaurus
|
.docusaurus
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ const config: Config = {
|
|||||||
mermaid: true,
|
mermaid: true,
|
||||||
},
|
},
|
||||||
themes: ['@docusaurus/theme-mermaid'],
|
themes: ['@docusaurus/theme-mermaid'],
|
||||||
plugins: [],
|
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'classic',
|
'classic',
|
||||||
@@ -76,11 +75,6 @@ const config: Config = {
|
|||||||
position: 'left',
|
position: 'left',
|
||||||
label: 'User Guides',
|
label: 'User Guides',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
href: 'https://docs.all-hands.dev/swagger-ui/', // FIXME: this should be a relative path, but docusarus steals the click
|
|
||||||
label: 'API',
|
|
||||||
position: 'left',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'localeDropdown',
|
type: 'localeDropdown',
|
||||||
position: 'left',
|
position: 'left',
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const swaggerUiDist = require('swagger-ui-dist');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This script manually sets up Swagger UI for the Docusaurus documentation.
|
|
||||||
*
|
|
||||||
* Why we need this approach:
|
|
||||||
* 1. Docusaurus doesn't have a built-in way to integrate Swagger UI
|
|
||||||
* 2. We need to copy the necessary files from swagger-ui-dist to our static directory
|
|
||||||
* 3. We need to create a custom index.html file that points to our OpenAPI spec
|
|
||||||
* 4. This approach allows us to customize the Swagger UI to match our documentation style
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Get the absolute path to the swagger-ui-dist package
|
|
||||||
const swaggerUiDistPath = swaggerUiDist.getAbsoluteFSPath();
|
|
||||||
|
|
||||||
// Create the target directory if it doesn't exist
|
|
||||||
const targetDir = path.join(__dirname, 'static', 'swagger-ui');
|
|
||||||
if (!fs.existsSync(targetDir)) {
|
|
||||||
fs.mkdirSync(targetDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy all files from swagger-ui-dist to our target directory
|
|
||||||
const files = fs.readdirSync(swaggerUiDistPath);
|
|
||||||
files.forEach(file => {
|
|
||||||
const sourcePath = path.join(swaggerUiDistPath, file);
|
|
||||||
const targetPath = path.join(targetDir, file);
|
|
||||||
|
|
||||||
// Skip directories and non-essential files
|
|
||||||
if (fs.statSync(sourcePath).isDirectory() ||
|
|
||||||
file === 'package.json' ||
|
|
||||||
file === 'README.md' ||
|
|
||||||
file.endsWith('.map')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.copyFileSync(sourcePath, targetPath);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a custom index.html file that points to our OpenAPI spec
|
|
||||||
const indexHtml = `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OpenHands API Documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
|
||||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
|
||||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: -moz-scrollbars-vertical;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
*,
|
|
||||||
*:before,
|
|
||||||
*:after {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="swagger-ui"></div>
|
|
||||||
|
|
||||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
|
||||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
|
||||||
<script>
|
|
||||||
window.onload = function() {
|
|
||||||
// Begin Swagger UI call region
|
|
||||||
const ui = SwaggerUIBundle({
|
|
||||||
url: "/openapi.json",
|
|
||||||
dom_id: '#swagger-ui',
|
|
||||||
deepLinking: true,
|
|
||||||
presets: [
|
|
||||||
SwaggerUIBundle.presets.apis,
|
|
||||||
SwaggerUIStandalonePreset
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
SwaggerUIBundle.plugins.DownloadUrl
|
|
||||||
],
|
|
||||||
layout: "StandaloneLayout"
|
|
||||||
});
|
|
||||||
// End Swagger UI call region
|
|
||||||
window.ui = ui;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`;
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(targetDir, 'index.html'), indexHtml);
|
|
||||||
|
|
||||||
console.log('Swagger UI files generated successfully in static/swagger-ui/');
|
|
||||||
@@ -52,7 +52,7 @@ LLM_API_KEY="sk_test_12345"
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -61,7 +61,7 @@ docker run -it \
|
|||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.cli
|
python -m openhands.core.cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -21,18 +21,14 @@ OpenHands fournit un mode Interface Graphique (GUI) convivial pour interagir ave
|
|||||||
3. Entrez la `Clé API` correspondante pour le fournisseur choisi.
|
3. Entrez la `Clé API` correspondante pour le fournisseur choisi.
|
||||||
4. Cliquez sur "Enregistrer" pour appliquer les paramètres.
|
4. Cliquez sur "Enregistrer" pour appliquer les paramètres.
|
||||||
|
|
||||||
### Jetons de Contrôle de Version
|
### Configuration du Jeton GitHub
|
||||||
|
|
||||||
OpenHands prend en charge plusieurs fournisseurs de contrôle de version. Vous pouvez configurer des jetons pour plusieurs fournisseurs simultanément.
|
|
||||||
|
|
||||||
#### Configuration du Jeton GitHub
|
|
||||||
|
|
||||||
OpenHands exporte automatiquement un `GITHUB_TOKEN` vers l'environnement shell s'il est disponible. Cela peut se produire de deux manières :
|
OpenHands exporte automatiquement un `GITHUB_TOKEN` vers l'environnement shell s'il est disponible. Cela peut se produire de deux manières :
|
||||||
|
|
||||||
1. **Localement (OSS)** : L'utilisateur saisit directement son jeton GitHub
|
1. **Localement (OSS)** : L'utilisateur saisit directement son jeton GitHub
|
||||||
2. **En ligne (SaaS)** : Le jeton est obtenu via l'authentification OAuth GitHub
|
2. **En ligne (SaaS)** : Le jeton est obtenu via l'authentification OAuth GitHub
|
||||||
|
|
||||||
##### Configuration d'un Jeton GitHub Local
|
#### Configuration d'un Jeton GitHub Local
|
||||||
|
|
||||||
1. **Générer un Personal Access Token (PAT)** :
|
1. **Générer un Personal Access Token (PAT)** :
|
||||||
- Allez dans Paramètres GitHub > Paramètres développeur > Personal Access Tokens > Tokens (classique)
|
- Allez dans Paramètres GitHub > Paramètres développeur > Personal Access Tokens > Tokens (classique)
|
||||||
@@ -44,11 +40,11 @@ OpenHands exporte automatiquement un `GITHUB_TOKEN` vers l'environnement shell s
|
|||||||
|
|
||||||
2. **Entrer le Jeton dans OpenHands** :
|
2. **Entrer le Jeton dans OpenHands** :
|
||||||
- Cliquez sur le bouton Paramètres (icône d'engrenage) en haut à droite
|
- Cliquez sur le bouton Paramètres (icône d'engrenage) en haut à droite
|
||||||
- Accédez à la section "Git Provider Settings"
|
- Accédez à la section "GitHub"
|
||||||
- Collez votre jeton dans le champ "Jeton GitHub"
|
- Collez votre jeton dans le champ "Jeton GitHub"
|
||||||
- Cliquez sur "Enregistrer" pour appliquer les modifications
|
- Cliquez sur "Enregistrer" pour appliquer les modifications
|
||||||
|
|
||||||
##### Politiques de Jetons Organisationnels
|
#### Politiques de Jetons Organisationnels
|
||||||
|
|
||||||
Si vous travaillez avec des dépôts organisationnels, une configuration supplémentaire peut être nécessaire :
|
Si vous travaillez avec des dépôts organisationnels, une configuration supplémentaire peut être nécessaire :
|
||||||
|
|
||||||
@@ -63,7 +59,7 @@ Si vous travaillez avec des dépôts organisationnels, une configuration supplé
|
|||||||
- Si nécessaire, cliquez sur "Activer SSO" à côté de votre organisation
|
- Si nécessaire, cliquez sur "Activer SSO" à côté de votre organisation
|
||||||
- Terminez le processus d'autorisation SSO
|
- Terminez le processus d'autorisation SSO
|
||||||
|
|
||||||
##### Authentification OAuth (Mode En Ligne)
|
#### Authentification OAuth (Mode En Ligne)
|
||||||
|
|
||||||
Lorsque vous utilisez OpenHands en mode en ligne, le flux OAuth GitHub :
|
Lorsque vous utilisez OpenHands en mode en ligne, le flux OAuth GitHub :
|
||||||
|
|
||||||
@@ -78,7 +74,7 @@ Lorsque vous utilisez OpenHands en mode en ligne, le flux OAuth GitHub :
|
|||||||
- Autorisez OpenHands à accéder à votre compte GitHub
|
- Autorisez OpenHands à accéder à votre compte GitHub
|
||||||
- Si vous utilisez une organisation, autorisez l'accès à l'organisation si vous y êtes invité
|
- Si vous utilisez une organisation, autorisez l'accès à l'organisation si vous y êtes invité
|
||||||
|
|
||||||
##### Dépannage
|
#### Dépannage
|
||||||
|
|
||||||
Problèmes courants et solutions :
|
Problèmes courants et solutions :
|
||||||
|
|
||||||
@@ -99,43 +95,6 @@ Problèmes courants et solutions :
|
|||||||
- Vérifiez la console du navigateur pour tout message d'erreur
|
- Vérifiez la console du navigateur pour tout message d'erreur
|
||||||
- Utilisez le bouton "Tester la connexion" dans les paramètres s'il est disponible
|
- Utilisez le bouton "Tester la connexion" dans les paramètres s'il est disponible
|
||||||
|
|
||||||
#### Configuration du Jeton GitLab
|
|
||||||
|
|
||||||
OpenHands exporte automatiquement un `GITLAB_TOKEN` vers l'environnement shell, uniquement pour les installations locales, s'il est disponible.
|
|
||||||
|
|
||||||
##### Configuration d'un Jeton GitLab
|
|
||||||
|
|
||||||
1. **Générer un Personal Access Token (PAT)** :
|
|
||||||
- Sur GitLab, allez dans Paramètres utilisateur > Jetons d'accès
|
|
||||||
- Créez un nouveau jeton avec les portées suivantes :
|
|
||||||
- `api` (Accès API)
|
|
||||||
- `read_user` (Lecture des informations utilisateur)
|
|
||||||
- `read_repository` (Lecture du dépôt)
|
|
||||||
- `write_repository` (Écriture du dépôt)
|
|
||||||
- Définissez une date d'expiration ou laissez vide pour un jeton sans expiration
|
|
||||||
|
|
||||||
2. **Entrer le Jeton dans OpenHands** :
|
|
||||||
- Cliquez sur le bouton Paramètres (icône d'engrenage)
|
|
||||||
- Accédez à la section `Git Provider Settings`
|
|
||||||
- Collez votre jeton dans le champ `Jeton GitLab`
|
|
||||||
- Si vous utilisez GitLab auto-hébergé, entrez l'URL de votre instance GitLab
|
|
||||||
- Cliquez sur `Enregistrer les modifications` pour appliquer les changements
|
|
||||||
|
|
||||||
##### Dépannage
|
|
||||||
|
|
||||||
Problèmes courants et solutions :
|
|
||||||
|
|
||||||
1. **Jeton Non Reconnu** :
|
|
||||||
- Assurez-vous que le jeton est correctement enregistré dans les paramètres
|
|
||||||
- Vérifiez que le jeton n'a pas expiré
|
|
||||||
- Vérifiez que le jeton a les portées requises
|
|
||||||
- Pour les instances auto-hébergées, vérifiez l'URL correcte de l'instance
|
|
||||||
|
|
||||||
2. **Accès Refusé** :
|
|
||||||
- Vérifiez les permissions d'accès au projet
|
|
||||||
- Vérifiez si le jeton possède les portées nécessaires
|
|
||||||
- Pour les dépôts de groupe/organisation, assurez-vous d'avoir les accès appropriés
|
|
||||||
|
|
||||||
### Paramètres Avancés
|
### Paramètres Avancés
|
||||||
|
|
||||||
1. Basculez sur `Options Avancées` pour accéder aux paramètres supplémentaires.
|
1. Basculez sur `Options Avancées` pour accéder aux paramètres supplémentaires.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ LLM_API_KEY="sk_test_12345"
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -56,6 +56,6 @@ docker run -it \
|
|||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
|
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -13,16 +13,16 @@
|
|||||||
La façon la plus simple d'exécuter OpenHands est avec Docker.
|
La façon la plus simple d'exécuter OpenHands est avec Docker.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik
|
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik
|
||||||
|
|
||||||
docker run -it --rm --pull=always \
|
docker run -it --rm --pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e LOG_ALL_EVENTS=true \
|
-e LOG_ALL_EVENTS=true \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app \
|
--name openhands-app \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34
|
docker.all-hands.dev/all-hands-ai/openhands:0.32
|
||||||
```
|
```
|
||||||
|
|
||||||
Vous pouvez également exécuter OpenHands en mode [headless scriptable](https://docs.all-hands.dev/modules/usage/how-to/headless-mode), en tant que [CLI interactive](https://docs.all-hands.dev/modules/usage/how-to/cli-mode), ou en utilisant l'[Action GitHub OpenHands](https://docs.all-hands.dev/modules/usage/how-to/github-action).
|
Vous pouvez également exécuter OpenHands en mode [headless scriptable](https://docs.all-hands.dev/modules/usage/how-to/headless-mode), en tant que [CLI interactive](https://docs.all-hands.dev/modules/usage/how-to/cli-mode), ou en utilisant l'[Action GitHub OpenHands](https://docs.all-hands.dev/modules/usage/how-to/github-action).
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ C'est le Runtime par défaut qui est utilisé lorsque vous démarrez OpenHands.
|
|||||||
|
|
||||||
```
|
```
|
||||||
docker run # ...
|
docker run # ...
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Docker で OpenHands を CLI モードで実行するには:
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -44,7 +44,7 @@ docker run -it \
|
|||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.cli
|
python -m openhands.core.cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,7 @@ OpenHandsは、AI アシスタントとやり取りするためのグラフィ
|
|||||||
3. 選択したプロバイダーに対応する`API Key`を入力します。
|
3. 選択したプロバイダーに対応する`API Key`を入力します。
|
||||||
4. `Save Changes`をクリックして設定を適用します。
|
4. `Save Changes`をクリックして設定を適用します。
|
||||||
|
|
||||||
### バージョン管理トークン
|
### GitHubトークンの設定
|
||||||
|
|
||||||
OpenHandsは複数のバージョン管理プロバイダーをサポートしています。複数のプロバイダーのトークンを同時に設定できます。
|
|
||||||
|
|
||||||
#### GitHubトークンの設定
|
|
||||||
|
|
||||||
OpenHandsは、利用可能な場合、自動的に`GITHUB_TOKEN`をシェル環境にエクスポートします。これは2つの方法で行われます。
|
OpenHandsは、利用可能な場合、自動的に`GITHUB_TOKEN`をシェル環境にエクスポートします。これは2つの方法で行われます。
|
||||||
|
|
||||||
@@ -38,7 +34,7 @@ OpenHandsは、利用可能な場合、自動的に`GITHUB_TOKEN`をシェル環
|
|||||||
- Minimal Permissions(検索用に**Meta Data = Read-only**を選択し、ブランチ作成用に**Pull Requests = Read and Write**、**Content = Read and Write**を選択します)
|
- Minimal Permissions(検索用に**Meta Data = Read-only**を選択し、ブランチ作成用に**Pull Requests = Read and Write**、**Content = Read and Write**を選択します)
|
||||||
2. **OpenHandsにトークンを入力**:
|
2. **OpenHandsにトークンを入力**:
|
||||||
- 設定ボタン(歯車アイコン)をクリックします。
|
- 設定ボタン(歯車アイコン)をクリックします。
|
||||||
- `Git Provider Settings`セクションに移動します。
|
- `GitHub Settings`セクションに移動します。
|
||||||
- `GitHub Token`フィールドにトークンを貼り付けます。
|
- `GitHub Token`フィールドにトークンを貼り付けます。
|
||||||
- `Save Changes`をクリックして変更を適用します。
|
- `Save Changes`をクリックして変更を適用します。
|
||||||
</details>
|
</details>
|
||||||
@@ -98,46 +94,6 @@ OpenHandsは、利用可能な場合、自動的に`GITHUB_TOKEN`をシェル環
|
|||||||
- 組織を使用している場合は、プロンプトが表示されたら組織へのアクセスを承認します。
|
- 組織を使用している場合は、プロンプトが表示されたら組織へのアクセスを承認します。
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
#### GitLabトークンの設定
|
|
||||||
|
|
||||||
OpenHandsは、利用可能な場合、ローカルインストールのみ、自動的に`GITLAB_TOKEN`をシェル環境にエクスポートします。
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>GitLabトークンの設定</summary>
|
|
||||||
|
|
||||||
1. **Personal Access Token(PAT)の生成**:
|
|
||||||
- GitLabで、User Settings > Access Tokensに移動します。
|
|
||||||
- 以下のスコープを持つ新しいトークンを作成します:
|
|
||||||
- `api`(APIアクセス)
|
|
||||||
- `read_user`(ユーザー情報の読み取り)
|
|
||||||
- `read_repository`(リポジトリ読み取り)
|
|
||||||
- `write_repository`(リポジトリ書き込み)
|
|
||||||
- 有効期限を設定するか、無期限トークンの場合は空白のままにします。
|
|
||||||
2. **OpenHandsにトークンを入力**:
|
|
||||||
- 設定ボタン(歯車アイコン)をクリックします。
|
|
||||||
- `Git Provider Settings`セクションに移動します。
|
|
||||||
- `GitLab Token`フィールドにトークンを貼り付けます。
|
|
||||||
- セルフホスト型GitLabを使用している場合は、GitLabインスタンスのURLを入力します。
|
|
||||||
- `Save Changes`をクリックして変更を適用します。
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>トラブルシューティング</summary>
|
|
||||||
|
|
||||||
一般的な問題と解決策:
|
|
||||||
|
|
||||||
- **トークンが認識されない**:
|
|
||||||
- トークンが設定に正しく保存されていることを確認します。
|
|
||||||
- トークンの有効期限が切れていないことを確認します。
|
|
||||||
- トークンに必要なスコープがあることを確認します。
|
|
||||||
- セルフホスト型インスタンスの場合は、正しいインスタンスURLを確認します。
|
|
||||||
|
|
||||||
- **アクセスが拒否された**:
|
|
||||||
- プロジェクトのアクセス権限を確認します。
|
|
||||||
- トークンに必要なスコープがあるかどうかを確認します。
|
|
||||||
- グループ/組織のリポジトリの場合は、適切なアクセス権があることを確認します。
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### 高度な設定
|
### 高度な設定
|
||||||
|
|
||||||
1. 設定ページ内で、`Advanced`オプションを切り替えて追加の設定にアクセスします。
|
1. 設定ページ内で、`Advanced`オプションを切り替えて追加の設定にアクセスします。
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ DockerでOpenHandsをヘッドレスモードで実行するには:
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -42,7 +42,7 @@ docker run -it \
|
|||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ nikolaik の `SANDBOX_RUNTIME_CONTAINER_IMAGE` は、ランタイムサーバー
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run # ...
|
docker run # ...
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-v $WORKSPACE_BASE:/opt/workspace_base \
|
-v $WORKSPACE_BASE:/opt/workspace_base \
|
||||||
@@ -82,5 +82,5 @@ docker network create openhands-network
|
|||||||
# 分離されたネットワークで OpenHands を実行
|
# 分離されたネットワークで OpenHands を実行
|
||||||
docker run # ... \
|
docker run # ... \
|
||||||
--network openhands-network \
|
--network openhands-network \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34
|
docker.all-hands.dev/all-hands-ai/openhands:0.32
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ Para executar o OpenHands no modo CLI com Docker:
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -45,7 +45,7 @@ docker run -it \
|
|||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.cli
|
python -m openhands.core.cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ O OpenHands fornece um modo de Interface Gráfica do Usuário (GUI) para interag
|
|||||||
3. Insira a `Chave de API` correspondente para o provedor escolhido.
|
3. Insira a `Chave de API` correspondente para o provedor escolhido.
|
||||||
4. Clique em `Salvar Alterações` para aplicar as configurações.
|
4. Clique em `Salvar Alterações` para aplicar as configurações.
|
||||||
|
|
||||||
### Tokens de Controle de Versão
|
### Configuração do Token do GitHub
|
||||||
|
|
||||||
O OpenHands suporta múltiplos provedores de controle de versão. Você pode configurar tokens para vários provedores simultaneamente.
|
|
||||||
|
|
||||||
#### Configuração do Token do GitHub
|
|
||||||
|
|
||||||
O OpenHands exporta automaticamente um `GITHUB_TOKEN` para o ambiente shell se ele estiver disponível. Isso pode acontecer de duas maneiras:
|
O OpenHands exporta automaticamente um `GITHUB_TOKEN` para o ambiente shell se ele estiver disponível. Isso pode acontecer de duas maneiras:
|
||||||
|
|
||||||
@@ -39,7 +35,7 @@ O OpenHands exporta automaticamente um `GITHUB_TOKEN` para o ambiente shell se e
|
|||||||
- Minimal Permissions (Selecione **Meta Data = Read-only** para pesquisa, **Pull Requests = Read and Write**, **Content = Read and Write** para criação de branches)
|
- Minimal Permissions (Selecione **Meta Data = Read-only** para pesquisa, **Pull Requests = Read and Write**, **Content = Read and Write** para criação de branches)
|
||||||
2. **Insira o Token no OpenHands**:
|
2. **Insira o Token no OpenHands**:
|
||||||
- Clique no botão Settings (ícone de engrenagem).
|
- Clique no botão Settings (ícone de engrenagem).
|
||||||
- Navegue até a seção `Git Provider Settings`.
|
- Navegue até a seção `GitHub Settings`.
|
||||||
- Cole seu token no campo `GitHub Token`.
|
- Cole seu token no campo `GitHub Token`.
|
||||||
- Clique em `Save Changes` para aplicar as alterações.
|
- Clique em `Save Changes` para aplicar as alterações.
|
||||||
</details>
|
</details>
|
||||||
@@ -99,46 +95,6 @@ O OpenHands exporta automaticamente um `GITHUB_TOKEN` para o ambiente shell se e
|
|||||||
- Se estiver usando uma organização, autorize o acesso à organização se solicitado.
|
- Se estiver usando uma organização, autorize o acesso à organização se solicitado.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
#### Configuração do Token do GitLab
|
|
||||||
|
|
||||||
O OpenHands exporta automaticamente um `GITLAB_TOKEN` para o ambiente shell, apenas para instalações locais, se ele estiver disponível.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Configurando um Token do GitLab</summary>
|
|
||||||
|
|
||||||
1. **Gere um Personal Access Token (PAT)**:
|
|
||||||
- No GitLab, vá para User Settings > Access Tokens.
|
|
||||||
- Crie um novo token com os seguintes escopos:
|
|
||||||
- `api` (Acesso à API)
|
|
||||||
- `read_user` (Leitura de informações do usuário)
|
|
||||||
- `read_repository` (Leitura do repositório)
|
|
||||||
- `write_repository` (Escrita no repositório)
|
|
||||||
- Defina uma data de expiração ou deixe em branco para um token sem expiração.
|
|
||||||
2. **Insira o Token no OpenHands**:
|
|
||||||
- Clique no botão Settings (ícone de engrenagem).
|
|
||||||
- Navegue até a seção `Git Provider Settings`.
|
|
||||||
- Cole seu token no campo `GitLab Token`.
|
|
||||||
- Se estiver usando GitLab auto-hospedado, insira a URL da sua instância GitLab.
|
|
||||||
- Clique em `Save Changes` para aplicar as alterações.
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Solução de Problemas</summary>
|
|
||||||
|
|
||||||
Problemas comuns e soluções:
|
|
||||||
|
|
||||||
- **Token Não Reconhecido**:
|
|
||||||
- Certifique-se de que o token esteja salvo corretamente nas configurações.
|
|
||||||
- Verifique se o token não expirou.
|
|
||||||
- Verifique se o token possui os escopos necessários.
|
|
||||||
- Para instâncias auto-hospedadas, verifique a URL correta da instância.
|
|
||||||
|
|
||||||
- **Acesso Negado**:
|
|
||||||
- Verifique as permissões de acesso ao projeto.
|
|
||||||
- Verifique se o token possui os escopos necessários.
|
|
||||||
- Para repositórios de grupo/organização, certifique-se de ter o acesso adequado.
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Configurações Avançadas
|
### Configurações Avançadas
|
||||||
|
|
||||||
1. Dentro da página Settings, ative as opções `Advanced` para acessar configurações adicionais.
|
1. Dentro da página Settings, ative as opções `Advanced` para acessar configurações adicionais.
|
||||||
|
|||||||
+2
-2
@@ -32,7 +32,7 @@ Para executar o OpenHands no modo Headless com Docker:
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -43,7 +43,7 @@ docker run -it \
|
|||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.main -t "escreva um script bash que imprima oi"
|
python -m openhands.core.main -t "escreva um script bash que imprima oi"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -58,17 +58,17 @@
|
|||||||
A maneira mais fácil de executar o OpenHands é no Docker.
|
A maneira mais fácil de executar o OpenHands é no Docker.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik
|
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik
|
||||||
|
|
||||||
docker run -it --rm --pull=always \
|
docker run -it --rm --pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e LOG_ALL_EVENTS=true \
|
-e LOG_ALL_EVENTS=true \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app \
|
--name openhands-app \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34
|
docker.all-hands.dev/all-hands-ai/openhands:0.32
|
||||||
```
|
```
|
||||||
|
|
||||||
Você encontrará o OpenHands em execução em http://localhost:3000!
|
Você encontrará o OpenHands em execução em http://localhost:3000!
|
||||||
|
|||||||
+4
-4
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Microagentes públicos são diretrizes especializadas acionadas por palavras-chave para todos os usuários do OpenHands.
|
Microagentes públicos são diretrizes especializadas acionadas por palavras-chave para todos os usuários do OpenHands.
|
||||||
Eles são definidos em arquivos markdown no diretório
|
Eles são definidos em arquivos markdown no diretório
|
||||||
[`microagents/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge).
|
[`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge).
|
||||||
|
|
||||||
Microagentes públicos:
|
Microagentes públicos:
|
||||||
- Monitoram comandos recebidos em busca de suas palavras-chave de acionamento.
|
- Monitoram comandos recebidos em busca de suas palavras-chave de acionamento.
|
||||||
@@ -15,7 +15,7 @@ Microagentes públicos:
|
|||||||
## Microagentes Públicos Atuais
|
## Microagentes Públicos Atuais
|
||||||
|
|
||||||
Para mais informações sobre microagentes específicos, consulte seus arquivos de documentação individuais no
|
Para mais informações sobre microagentes específicos, consulte seus arquivos de documentação individuais no
|
||||||
diretório [`microagents/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/).
|
diretório [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge/).
|
||||||
|
|
||||||
### Agente GitHub
|
### Agente GitHub
|
||||||
**Arquivo**: `github.md`
|
**Arquivo**: `github.md`
|
||||||
@@ -59,7 +59,7 @@ yes | npm install package-name
|
|||||||
## Contribuindo com um Microagente Público
|
## Contribuindo com um Microagente Público
|
||||||
|
|
||||||
Você pode criar seus próprios microagentes públicos adicionando novos arquivos markdown ao
|
Você pode criar seus próprios microagentes públicos adicionando novos arquivos markdown ao
|
||||||
diretório [`microagents/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/).
|
diretório [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge/).
|
||||||
|
|
||||||
### Melhores Práticas para Microagentes Públicos
|
### Melhores Práticas para Microagentes Públicos
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ Antes de criar um microagente público, considere:
|
|||||||
|
|
||||||
#### 2. Crie o Arquivo
|
#### 2. Crie o Arquivo
|
||||||
|
|
||||||
Crie um novo arquivo markdown em [`microagents/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/)
|
Crie um novo arquivo markdown em [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge/)
|
||||||
com um nome descritivo (por exemplo, `docker.md` para um agente focado em Docker).
|
com um nome descritivo (por exemplo, `docker.md` para um agente focado em Docker).
|
||||||
|
|
||||||
Atualize o arquivo com o frontmatter necessário [de acordo com o formato exigido](./microagents-overview#microagent-format)
|
Atualize o arquivo com o frontmatter necessário [de acordo com o formato exigido](./microagents-overview#microagent-format)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Este é o Runtime padrão que é usado quando você inicia o OpenHands. Você po
|
|||||||
|
|
||||||
```
|
```
|
||||||
docker run # ...
|
docker run # ...
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ LLM_API_KEY="sk_test_12345"
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -59,7 +59,7 @@ docker run -it \
|
|||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.cli
|
python -m openhands.core.cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -19,18 +19,14 @@ OpenHands 提供了一个用户友好的图形用户界面(GUI)模式,用
|
|||||||
3. 输入所选提供商对应的 `API Key`。
|
3. 输入所选提供商对应的 `API Key`。
|
||||||
4. 点击"保存"应用设置。
|
4. 点击"保存"应用设置。
|
||||||
|
|
||||||
### 版本控制令牌
|
### GitHub Token 设置
|
||||||
|
|
||||||
OpenHands 支持多个版本控制提供商。您可以同时配置多个提供商的令牌。
|
|
||||||
|
|
||||||
#### GitHub Token 设置
|
|
||||||
|
|
||||||
如果可用,OpenHands 会自动将 `GITHUB_TOKEN` 导出到 shell 环境中。这可以通过两种方式实现:
|
如果可用,OpenHands 会自动将 `GITHUB_TOKEN` 导出到 shell 环境中。这可以通过两种方式实现:
|
||||||
|
|
||||||
1. **本地(OSS)**:用户直接输入他们的 GitHub token
|
1. **本地(OSS)**:用户直接输入他们的 GitHub token
|
||||||
2. **在线(SaaS)**:通过 GitHub OAuth 身份验证获取 token
|
2. **在线(SaaS)**:通过 GitHub OAuth 身份验证获取 token
|
||||||
|
|
||||||
##### 设置本地 GitHub Token
|
#### 设置本地 GitHub Token
|
||||||
|
|
||||||
1. **生成个人访问令牌(PAT)**:
|
1. **生成个人访问令牌(PAT)**:
|
||||||
- 转到 GitHub 设置 > 开发者设置 > 个人访问令牌 > 令牌(经典)
|
- 转到 GitHub 设置 > 开发者设置 > 个人访问令牌 > 令牌(经典)
|
||||||
@@ -42,11 +38,11 @@ OpenHands 支持多个版本控制提供商。您可以同时配置多个提供
|
|||||||
|
|
||||||
2. **在 OpenHands 中输入令牌**:
|
2. **在 OpenHands 中输入令牌**:
|
||||||
- 点击右上角的设置按钮(齿轮图标)
|
- 点击右上角的设置按钮(齿轮图标)
|
||||||
- 导航到"Git Provider Settings"部分
|
- 导航到"GitHub"部分
|
||||||
- 将令牌粘贴到"GitHub Token"字段中
|
- 将令牌粘贴到"GitHub Token"字段中
|
||||||
- 点击"保存"应用更改
|
- 点击"保存"应用更改
|
||||||
|
|
||||||
##### 组织令牌策略
|
#### 组织令牌策略
|
||||||
|
|
||||||
如果您使用组织仓库,可能需要额外的设置:
|
如果您使用组织仓库,可能需要额外的设置:
|
||||||
|
|
||||||
@@ -61,7 +57,7 @@ OpenHands 支持多个版本控制提供商。您可以同时配置多个提供
|
|||||||
- 如果需要,点击组织旁边的"启用 SSO"
|
- 如果需要,点击组织旁边的"启用 SSO"
|
||||||
- 完成 SSO 授权过程
|
- 完成 SSO 授权过程
|
||||||
|
|
||||||
##### OAuth 身份验证(在线模式)
|
#### OAuth 身份验证(在线模式)
|
||||||
|
|
||||||
在在线模式下使用 OpenHands 时,GitHub OAuth 流程:
|
在在线模式下使用 OpenHands 时,GitHub OAuth 流程:
|
||||||
|
|
||||||
@@ -76,7 +72,7 @@ OpenHands 支持多个版本控制提供商。您可以同时配置多个提供
|
|||||||
- 授权 OpenHands 访问您的 GitHub 帐户
|
- 授权 OpenHands 访问您的 GitHub 帐户
|
||||||
- 如果使用组织,在出现提示时授权组织访问
|
- 如果使用组织,在出现提示时授权组织访问
|
||||||
|
|
||||||
##### 故障排除
|
#### 故障排除
|
||||||
|
|
||||||
常见问题和解决方案:
|
常见问题和解决方案:
|
||||||
|
|
||||||
@@ -97,43 +93,6 @@ OpenHands 支持多个版本控制提供商。您可以同时配置多个提供
|
|||||||
- 检查浏览器控制台中是否有任何错误消息
|
- 检查浏览器控制台中是否有任何错误消息
|
||||||
- 如果可用,使用设置中的"测试连接"按钮
|
- 如果可用,使用设置中的"测试连接"按钮
|
||||||
|
|
||||||
#### GitLab Token 设置
|
|
||||||
|
|
||||||
OpenHands 会自动将 `GITLAB_TOKEN` 导出到 shell 环境中,仅适用于本地安装,如果它可用的话。
|
|
||||||
|
|
||||||
##### 设置 GitLab Token
|
|
||||||
|
|
||||||
1. **生成个人访问令牌(PAT)**:
|
|
||||||
- 在 GitLab 中,转到用户设置 > 访问令牌
|
|
||||||
- 创建具有以下范围的新令牌:
|
|
||||||
- `api`(API 访问)
|
|
||||||
- `read_user`(读取用户信息)
|
|
||||||
- `read_repository`(读取仓库)
|
|
||||||
- `write_repository`(写入仓库)
|
|
||||||
- 设置过期日期或留空以获取永不过期的令牌
|
|
||||||
|
|
||||||
2. **在 OpenHands 中输入令牌**:
|
|
||||||
- 点击设置按钮(齿轮图标)
|
|
||||||
- 导航到 `Git Provider Settings` 部分
|
|
||||||
- 将令牌粘贴到 `GitLab Token` 字段中
|
|
||||||
- 如果使用自托管 GitLab,请输入您的 GitLab 实例 URL
|
|
||||||
- 点击 `Save Changes` 应用更改
|
|
||||||
|
|
||||||
##### 故障排除
|
|
||||||
|
|
||||||
常见问题和解决方案:
|
|
||||||
|
|
||||||
1. **令牌无法识别**:
|
|
||||||
- 确保令牌已正确保存在设置中
|
|
||||||
- 检查令牌是否已过期
|
|
||||||
- 验证令牌是否具有所需的范围
|
|
||||||
- 对于自托管实例,验证正确的实例 URL
|
|
||||||
|
|
||||||
2. **访问被拒绝**:
|
|
||||||
- 验证项目访问权限
|
|
||||||
- 检查令牌是否具有必要的范围
|
|
||||||
- 对于组/组织仓库,确保您拥有适当的访问权限
|
|
||||||
|
|
||||||
### 高级设置
|
### 高级设置
|
||||||
|
|
||||||
1. 切换`高级选项`以访问其他设置。
|
1. 切换`高级选项`以访问其他设置。
|
||||||
|
|||||||
+2
-2
@@ -47,7 +47,7 @@ LLM_API_KEY="sk_test_12345"
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -57,6 +57,6 @@ docker run -it \
|
|||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
|
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,16 +11,16 @@
|
|||||||
在 Docker 中运行 OpenHands 是最简单的方式。
|
在 Docker 中运行 OpenHands 是最简单的方式。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik
|
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik
|
||||||
|
|
||||||
docker run -it --rm --pull=always \
|
docker run -it --rm --pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e LOG_ALL_EVENTS=true \
|
-e LOG_ALL_EVENTS=true \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app \
|
--name openhands-app \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34
|
docker.all-hands.dev/all-hands-ai/openhands:0.32
|
||||||
```
|
```
|
||||||
|
|
||||||
你也可以在可脚本化的[无头模式](https://docs.all-hands.dev/modules/usage/how-to/headless-mode)下运行 OpenHands,作为[交互式 CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode),或使用 [OpenHands GitHub Action](https://docs.all-hands.dev/modules/usage/how-to/github-action)。
|
你也可以在可脚本化的[无头模式](https://docs.all-hands.dev/modules/usage/how-to/headless-mode)下运行 OpenHands,作为[交互式 CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode),或使用 [OpenHands GitHub Action](https://docs.all-hands.dev/modules/usage/how-to/github-action)。
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
docker run # ...
|
docker run # ...
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,22 +8,18 @@ OpenHands Cloud can be accessed at https://app.all-hands.dev/.
|
|||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
After visiting OpenHands Cloud, you will be asked to connect with your GitHub or GitLab account:
|
After visiting OpenHands Cloud, you will be asked to connect with your GitHub account:
|
||||||
|
1. After reading and accepting the terms of service, click `Connect to GitHub`.
|
||||||
1. After reading and accepting the terms of service, click `Log in with GitHub` or `Log in with GitLab`.
|
|
||||||
2. Review the permissions requested by OpenHands and then click `Authorize OpenHands AI`.
|
2. Review the permissions requested by OpenHands and then click `Authorize OpenHands AI`.
|
||||||
- OpenHands will require some permissions from your GitHub or GitLab account. To read more about these permissions:
|
- OpenHands will require some permissions from your GitHub account. To read more about these permissions,
|
||||||
- GitHub: You can click the `Learn more` link on the GitHub authorize page.
|
you can click the `Learn more` link on the GitHub authorize page.
|
||||||
- GitLab: You can expand each permission request on the GitLab authorize page.
|
|
||||||
|
|
||||||
## Repository Access
|
## Repository Access
|
||||||
|
|
||||||
### GitHub
|
### Adding Repository Access
|
||||||
|
|
||||||
#### Adding Repository Access
|
|
||||||
|
|
||||||
You can grant OpenHands specific repository access:
|
You can grant OpenHands specific repository access:
|
||||||
1. Click `Add GitHub repos` on the Home page.
|
1. Click the `Select a GitHub project` dropdown, select `Add more repositories...`.
|
||||||
2. Select the organization, then choose the specific repositories to grant OpenHands access to.
|
2. Select the organization, then choose the specific repositories to grant OpenHands access to.
|
||||||
<details>
|
<details>
|
||||||
<summary>Permission Details for Repository Access</summary>
|
<summary>Permission Details for Repository Access</summary>
|
||||||
@@ -46,15 +42,11 @@ You can grant OpenHands specific repository access:
|
|||||||
|
|
||||||
3. Click on `Install & Authorize`.
|
3. Click on `Install & Authorize`.
|
||||||
|
|
||||||
#### Modifying Repository Access
|
### Modifying Repository Access
|
||||||
|
|
||||||
You can modify GitHub repository access at any time by:
|
You can modify repository access at any time by:
|
||||||
* Using the same `Add GitHub repos` workflow, or
|
* Using the same `Select a GitHub project > Add more repositories` workflow, or
|
||||||
* Visiting the Settings page and selecting `Configure GitHub Repositories` under the `Git Settings` section.
|
* Visiting the Settings page and selecting `Configure GitHub Repositories` under the `GitHub Settings` section.
|
||||||
|
|
||||||
### GitLab
|
|
||||||
|
|
||||||
When using your GitLab account, OpenHands will automatically have access to your repositories.
|
|
||||||
|
|
||||||
## Conversation Persistence
|
## Conversation Persistence
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
# Repository Customization
|
# Repository Customization
|
||||||
|
|
||||||
You can customize how OpenHands interacts with your repository by creating a
|
You can customize how OpenHands works with your repository by creating a
|
||||||
`.openhands` directory at the root level.
|
`.openhands` directory at the root level.
|
||||||
|
|
||||||
## Microagents
|
## Microagents
|
||||||
|
You can use microagents to extend the OpenHands prompts with information
|
||||||
Microagents allow you to extend OpenHands prompts with information specific to your project and define how OpenHands
|
about your project and how you want OpenHands to work. See
|
||||||
should function. See [Microagents Overview](../prompting/microagents-overview) for more information.
|
[Repository Microagents](../prompting/microagents-repo) for more information.
|
||||||
|
|
||||||
|
|
||||||
## Setup Script
|
## Setup Script
|
||||||
You can add a `.openhands/setup.sh` file, which will run every time OpenHands begins working with your repository.
|
You can add `.openhands/setup.sh`, which will be run every time OpenHands begins
|
||||||
This is an ideal location for installing dependencies, setting environment variables, and performing other setup tasks.
|
working with your repository. This is a good place to install dependencies, set
|
||||||
|
environment variables, etc.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ To run OpenHands in CLI mode with Docker:
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -45,7 +45,7 @@ docker run -it \
|
|||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.cli
|
python -m openhands.core.cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# Custom Sandbox
|
# Custom Sandbox
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
This guide is for users that would like to use their own custom Docker image for the runtime. For example
|
This guide is for users that would like to use their own custom Docker image for the runtime, e.g. with certain tools or programming languages pre-installed
|
||||||
with certain tools or programming languages pre-installed.
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
The sandbox is where the agent performs its tasks. Instead of running commands directly on your computer
|
The sandbox is where the agent performs its tasks. Instead of running commands directly on your computer
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# Using GitLab CI Runners
|
|
||||||
@@ -18,14 +18,11 @@ OpenHands provides a Graphical User Interface (GUI) mode for interacting with th
|
|||||||
3. Enter the corresponding `API Key` for your chosen provider.
|
3. Enter the corresponding `API Key` for your chosen provider.
|
||||||
4. Click `Save Changes` to apply the settings.
|
4. Click `Save Changes` to apply the settings.
|
||||||
|
|
||||||
### Version Control Tokens
|
### GitHub Token Setup
|
||||||
|
|
||||||
OpenHands supports multiple version control providers. You can configure tokens for multiple providers simultaneously.
|
OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if it is available. This can happen in two ways:
|
||||||
|
|
||||||
#### GitHub Token Setup
|
|
||||||
|
|
||||||
OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if provided:
|
|
||||||
|
|
||||||
|
**Local Installation**: The user directly inputs their GitHub token.
|
||||||
<details>
|
<details>
|
||||||
<summary>Setting Up a GitHub Token</summary>
|
<summary>Setting Up a GitHub Token</summary>
|
||||||
|
|
||||||
@@ -39,8 +36,9 @@ OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if pro
|
|||||||
- Minimal Permissions ( Select `Meta Data = Read-only` read for search, `Pull Requests = Read and Write` and `Content = Read and Write` for branch creation)
|
- Minimal Permissions ( Select `Meta Data = Read-only` read for search, `Pull Requests = Read and Write` and `Content = Read and Write` for branch creation)
|
||||||
2. **Enter Token in OpenHands**:
|
2. **Enter Token in OpenHands**:
|
||||||
- Click the Settings button (gear icon).
|
- Click the Settings button (gear icon).
|
||||||
|
- Navigate to the `GitHub Settings` section.
|
||||||
- Paste your token in the `GitHub Token` field.
|
- Paste your token in the `GitHub Token` field.
|
||||||
- Click `Save` to apply the changes.
|
- Click `Save Changes` to apply the changes.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -81,43 +79,21 @@ OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if pro
|
|||||||
- Check the browser console for any error messages.
|
- Check the browser console for any error messages.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
#### GitLab Token Setup
|
**OpenHands Cloud**: The token is obtained through GitHub OAuth authentication.
|
||||||
|
|
||||||
OpenHands automatically exports a `GITLAB_TOKEN` to the shell environment if provided:
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Setting Up a GitLab Token</summary>
|
<summary>OAuth Authentication</summary>
|
||||||
|
|
||||||
1. **Generate a Personal Access Token (PAT)**:
|
When using OpenHands Cloud, the GitHub OAuth flow requests the following permissions:
|
||||||
- On GitLab, go to User Settings > Access Tokens.
|
- Repository access (read/write)
|
||||||
- Create a new token with the following scopes:
|
- Workflow management
|
||||||
- `api` (API access)
|
- Organization read access
|
||||||
- `read_user` (Read user information)
|
|
||||||
- `read_repository` (Read repository)
|
|
||||||
- `write_repository` (Write repository)
|
|
||||||
- Set an expiration date or leave it blank for a non-expiring token.
|
|
||||||
2. **Enter Token in OpenHands**:
|
|
||||||
- Click the Settings button (gear icon).
|
|
||||||
- Paste your token in the `GitLab Token` field.
|
|
||||||
- Enter your GitLab instance URL if using self-hosted GitLab.
|
|
||||||
- Click `Save` to apply the changes.
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
To authenticate OpenHands:
|
||||||
<summary>Troubleshooting</summary>
|
- Click `Sign in with GitHub` when prompted.
|
||||||
|
- Review the requested permissions.
|
||||||
Common issues and solutions:
|
- Authorize OpenHands to access your GitHub account.
|
||||||
|
- If using an organization, authorize organization access if prompted.
|
||||||
- **Token Not Recognized**:
|
|
||||||
- Ensure the token is properly saved in settings.
|
|
||||||
- Check that the token hasn't expired.
|
|
||||||
- Verify the token has the required scopes.
|
|
||||||
- For self-hosted instances, verify the correct instance URL.
|
|
||||||
|
|
||||||
- **Access Denied**:
|
|
||||||
- Verify project access permissions.
|
|
||||||
- Check if the token has the necessary scopes.
|
|
||||||
- For group/organization repositories, ensure you have proper access.
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Advanced Settings
|
### Advanced Settings
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ To run OpenHands in Headless mode with Docker:
|
|||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
--pull=always \
|
--pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e SANDBOX_USER_ID=$(id -u) \
|
-e SANDBOX_USER_ID=$(id -u) \
|
||||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||||
-e LLM_API_KEY=$LLM_API_KEY \
|
-e LLM_API_KEY=$LLM_API_KEY \
|
||||||
@@ -43,7 +43,7 @@ docker run -it \
|
|||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34 \
|
docker.all-hands.dev/all-hands-ai/openhands:0.32 \
|
||||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -58,17 +58,17 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
|
|||||||
The easiest way to run OpenHands is in Docker.
|
The easiest way to run OpenHands is in Docker.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik
|
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik
|
||||||
|
|
||||||
docker run -it --rm --pull=always \
|
docker run -it --rm --pull=always \
|
||||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.34-nikolaik \
|
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.32-nikolaik \
|
||||||
-e LOG_ALL_EVENTS=true \
|
-e LOG_ALL_EVENTS=true \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-v ~/.openhands-state:/.openhands-state \
|
-v ~/.openhands-state:/.openhands-state \
|
||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--add-host host.docker.internal:host-gateway \
|
--add-host host.docker.internal:host-gateway \
|
||||||
--name openhands-app \
|
--name openhands-app \
|
||||||
docker.all-hands.dev/all-hands-ai/openhands:0.34
|
docker.all-hands.dev/all-hands-ai/openhands:0.32
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll find OpenHands running at http://localhost:3000!
|
You'll find OpenHands running at http://localhost:3000!
|
||||||
|
|||||||
@@ -6,26 +6,23 @@
|
|||||||
- Displays the conversation between the user and OpenHands.
|
- Displays the conversation between the user and OpenHands.
|
||||||
- OpenHands explains its actions in this panel.
|
- OpenHands explains its actions in this panel.
|
||||||
|
|
||||||
### Changes
|
|
||||||
- Shows the file changes performed by OpenHands.
|
|
||||||
|
|
||||||
### Workspace
|
### Workspace
|
||||||
- Browse project files and directories.
|
- Browse project files and directories.
|
||||||
- Use the `Open in VS Code` option to:
|
- Use the `Open in VS Code` option to:
|
||||||
* Modify files
|
* Modify files
|
||||||
* Upload and download files
|
* Upload and download files
|
||||||
|
|
||||||
### Terminal
|
|
||||||
- A space for OpenHands and users to run terminal commands.
|
|
||||||
|
|
||||||
### Jupyter
|
### Jupyter
|
||||||
- Shows all Python commands that were executed by OpenHands.
|
- Shows all Python commands that were executed by OpenHands.
|
||||||
- Particularly handy when using OpenHands to perform data visualization tasks.
|
- Particularly handy when using OpenHands to perform data visualization tasks.
|
||||||
|
|
||||||
### App
|
### App
|
||||||
- Displays the web server when OpenHands runs an application.
|
- Shows the web server when OpenHands runs an application.
|
||||||
- Users can interact with the running application.
|
- Users can interact with the running application.
|
||||||
|
|
||||||
### Browser
|
### Browser
|
||||||
- Used by OpenHands to browse websites.
|
- Used by OpenHands to browse websites.
|
||||||
- The browser is non-interactive.
|
- The browser is non-interactive.
|
||||||
|
|
||||||
|
### Terminal
|
||||||
|
- A space for OpenHands and users to run terminal commands.
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ Based on these findings and community feedback, the following models have been v
|
|||||||
- [gemini/gemini-2.5-pro](https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/)
|
- [gemini/gemini-2.5-pro](https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/)
|
||||||
- [deepseek/deepseek-chat](https://api-docs.deepseek.com/)
|
- [deepseek/deepseek-chat](https://api-docs.deepseek.com/)
|
||||||
- [openai/o3-mini](https://openai.com/index/openai-o3-mini/)
|
- [openai/o3-mini](https://openai.com/index/openai-o3-mini/)
|
||||||
- [openai/o3](https://openai.com/index/introducing-o3-and-o4-mini/)
|
|
||||||
- [openai/o4-mini](https://openai.com/index/introducing-o3-and-o4-mini/)
|
|
||||||
- [all-hands/openhands-lm-32b-v0.1](https://www.all-hands.dev/blog/introducing-openhands-lm-32b----a-strong-open-coding-agent-model) -- available through [OpenRouter](https://openrouter.ai/all-hands/openhands-lm-32b-v0.1)
|
- [all-hands/openhands-lm-32b-v0.1](https://www.all-hands.dev/blog/introducing-openhands-lm-32b----a-strong-open-coding-agent-model) -- available through [OpenRouter](https://openrouter.ai/all-hands/openhands-lm-32b-v0.1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ It is highly recommended that you use GPUs to serve local models for optimal exp
|
|||||||
For example, to download [OpenHands LM 32B v0.1](https://huggingface.co/all-hands/openhands-lm-32b-v0.1):
|
For example, to download [OpenHands LM 32B v0.1](https://huggingface.co/all-hands/openhands-lm-32b-v0.1):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
huggingface-cli download all-hands/openhands-lm-32b-v0.1 --local-dir all-hands/openhands-lm-32b-v0.1
|
huggingface-cli download all-hands/openhands-lm-32b-v0.1 --local-dir my_folder/openhands-lm-32b-v0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create an OpenAI-Compatible Endpoint With a Model Serving Framework
|
## Create an OpenAI-Compatible Endpoint With a Model Serving Framework
|
||||||
@@ -27,7 +27,7 @@ huggingface-cli download all-hands/openhands-lm-32b-v0.1 --local-dir all-hands/o
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1 python3 -m sglang.launch_server \
|
SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1 python3 -m sglang.launch_server \
|
||||||
--model all-hands/openhands-lm-32b-v0.1 \
|
--model my_folder/openhands-lm-32b-v0.1 \
|
||||||
--served-model-name openhands-lm-32b-v0.1 \
|
--served-model-name openhands-lm-32b-v0.1 \
|
||||||
--port 8000 \
|
--port 8000 \
|
||||||
--tp 2 --dp 1 \
|
--tp 2 --dp 1 \
|
||||||
@@ -41,7 +41,7 @@ SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1 python3 -m sglang.launch_server \
|
|||||||
- Example launch command for OpenHands LM 32B (with at least 2 GPUs):
|
- Example launch command for OpenHands LM 32B (with at least 2 GPUs):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
vllm serve all-hands/openhands-lm-32b-v0.1 \
|
vllm serve my_folder/openhands-lm-32b-v0.1 \
|
||||||
--host 0.0.0.0 --port 8000 \
|
--host 0.0.0.0 --port 8000 \
|
||||||
--api-key mykey \
|
--api-key mykey \
|
||||||
--tensor-parallel-size 2 \
|
--tensor-parallel-size 2 \
|
||||||
@@ -67,7 +67,7 @@ Ensure `config.toml` exists by running `make setup-config` which will create one
|
|||||||
workspace_base="/path/to/your/workspace"
|
workspace_base="/path/to/your/workspace"
|
||||||
|
|
||||||
[llm]
|
[llm]
|
||||||
model="openhands-lm-32b-v0.1"
|
embedding_model="local"
|
||||||
ollama_base_url="http://localhost:8000"
|
ollama_base_url="http://localhost:8000"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
# Keyword-Triggered Microagents
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Keyword-triggered microagents provide OpenHands with specific instructions that are activated when certain keywords
|
|
||||||
appear in the prompt. This is useful for tailoring behavior based on particular tools, languages, or frameworks.
|
|
||||||
|
|
||||||
## Microagent File
|
|
||||||
|
|
||||||
Create a keyword-triggered microagent (example: `.openhands/microagents/trigger-keyword.md`) to include instructions
|
|
||||||
that activate only for prompts with specific keywords.
|
|
||||||
|
|
||||||
## Frontmatter Syntax
|
|
||||||
|
|
||||||
Frontmatter is required for keyword-triggered microagents. It must be placed at the top of the file,
|
|
||||||
above the guidelines.
|
|
||||||
|
|
||||||
Enclose the frontmatter in triple dashes (---) and include the following fields:
|
|
||||||
|
|
||||||
| Field | Description | Required | Default |
|
|
||||||
|------------|--------------------------------------------------|----------|------------------|
|
|
||||||
| `name` | A unique identifier for the microagent. | Yes | 'default' |
|
|
||||||
| `type` | Type of microagent. Must be set to `knowledge`. | Yes | 'repo' |
|
|
||||||
| `triggers` | A list of keywords that activate the microagent. | Yes | None |
|
|
||||||
| `agent` | The agent this microagent applies to. | No | 'CodeActAgent' |
|
|
||||||
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```
|
|
||||||
---
|
|
||||||
name: magic_word
|
|
||||||
type: knowledge
|
|
||||||
triggers:
|
|
||||||
- yummyhappy
|
|
||||||
- happyyummy
|
|
||||||
agent: CodeActAgent
|
|
||||||
---
|
|
||||||
|
|
||||||
The user has said the magic word. Respond with "That was delicious!"
|
|
||||||
```
|
|
||||||
|
|
||||||
Keyword-triggered microagents:
|
|
||||||
- Monitor incoming prompts for specified trigger words.
|
|
||||||
- Activate when relevant triggers are detected.
|
|
||||||
- Apply their specialized knowledge and capabilities.
|
|
||||||
- Follow defined guidelines and restrictions.
|
|
||||||
|
|
||||||
[See examples of microagents triggered by keywords in the official OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge)
|
|
||||||
@@ -1,40 +1,31 @@
|
|||||||
# Microagents Overview
|
# Microagents Overview
|
||||||
|
|
||||||
Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge.
|
Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge, repository-specific context
|
||||||
They provide expert guidance, automate common tasks, and ensure consistent practices across projects.
|
and task-specific workflows. They help by providing expert guidance, automating common tasks, and ensuring
|
||||||
|
consistent practices across projects.
|
||||||
|
|
||||||
## Microagent Types
|
## Microagent Categories
|
||||||
|
|
||||||
Currently OpenHands supports the following types of microagents:
|
Currently OpenHands supports two categories of microagents:
|
||||||
|
|
||||||
- [General Repository Microagents](./microagents-repo): General guidelines for OpenHands about the repository.
|
- [Repository-specific Microagents](./microagents-repo): Repository-specific context and guidelines for OpenHands.
|
||||||
- [Keyword-Triggered Microagents](./microagents-keyword): Guidelines activated by specific keywords in prompts.
|
- [Public Microagents](./microagents-public): General guidelines triggered by keywords for all OpenHands users.
|
||||||
|
|
||||||
To customize OpenHands' behavior, create a .openhands/microagents/ directory in the root of your repository and
|
A microagent is classified as repository-specific or public depending on its location:
|
||||||
add `<microagent_name>.md` files inside.
|
|
||||||
|
|
||||||
:::note
|
- Repository-specific microagents are located in a repository's `.openhands/microagents/` directory
|
||||||
Loaded microagents take up space in the context window.
|
- Public microagents are located in the official OpenHands repository inside the `/microagents` folder
|
||||||
These microagents, alongside user messages, inform OpenHands about the task and the environment.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Example repository structure:
|
When OpenHands works with a repository, it:
|
||||||
|
|
||||||
```
|
1. Loads **repository-specific** microagents from `.openhands/microagents/` if present in the repository.
|
||||||
some-repository/
|
2. Loads **public knowledge** microagents triggered by keywords in conversations
|
||||||
└── .openhands/
|
3. Loads **public tasks** microagents when explicitly requested by the user
|
||||||
└── microagents/
|
|
||||||
└── repo.md # General repository guidelines
|
|
||||||
└── trigger_this.md # Microagent triggered by specific keywords
|
|
||||||
└── trigger_that.md # Microagent triggered by specific keywords
|
|
||||||
```
|
|
||||||
|
|
||||||
## Microagents Frontmatter Requirements
|
You can check out the existing public microagents at the [official OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/).
|
||||||
|
|
||||||
Each microagent file may include frontmatter that provides additional information. In some cases, this frontmatter
|
## Microagent Format
|
||||||
is required:
|
|
||||||
|
|
||||||
| Microagent Type | Frontmatter Requirement |
|
All microagents use markdown files with YAML frontmatter that have special instructions to help OpenHands activate them.
|
||||||
|----------------------------------|-------------------------------------------------------|
|
|
||||||
| `General Repository Microagents` | Required only if more than one of this type exists. |
|
Check out the [syntax documentation](./microagents-syntax) for a comprehensive guide on how to configure your microagents.
|
||||||
| `Keyword-Triggered Microagents` | Required. |
|
|
||||||
|
|||||||
@@ -1,16 +1,35 @@
|
|||||||
# Global Microagents
|
# Public Microagents
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Global microagents are [keyword-triggered microagents](./microagents-keyword) that apply to all OpenHands users.
|
Public microagents provide specialized context and capabilities for all OpenHands users, regardless of their repository configuration. Unlike repository-specific microagents, public microagents are globally available across all repositories.
|
||||||
|
|
||||||
## Contributing a Global Microagent
|
Public microagents come in two types:
|
||||||
|
|
||||||
You can create global microagents and share with the community by opening a pull request to the official repository.
|
- **Knowledge microagents**: Automatically activated when keywords in conversations match their triggers
|
||||||
|
- **Task microagents**: Explicitly invoked by users to guide through specific workflows
|
||||||
|
|
||||||
|
Both types follow the same syntax and structure as repository-specific microagents, using markdown files with YAML frontmatter that define their behavior and capabilities. They are located in the official OpenHands repository under:
|
||||||
|
|
||||||
|
- [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge) for knowledge microagents
|
||||||
|
- [`microagents/tasks/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/tasks) for task microagents
|
||||||
|
|
||||||
|
Public microagents:
|
||||||
|
|
||||||
|
- Monitor incoming commands for their trigger words.
|
||||||
|
- Activate when relevant triggers are detected.
|
||||||
|
- Apply their specialized knowledge and capabilities.
|
||||||
|
- Follow their specific guidelines and restrictions.
|
||||||
|
|
||||||
|
When loading public microagents, OpenHands scans the official repository's microagents directories recursively, processing all markdown files except README.md. The system categorizes each microagent based on its `type` field in the YAML frontmatter, regardless of its exact file location within the knowledge or tasks directories.
|
||||||
|
|
||||||
|
## Contributing a Public Microagent
|
||||||
|
|
||||||
|
You can create public microagents and share with the community by opening a pull request to the official repository.
|
||||||
|
|
||||||
See the [CONTRIBUTING.md](https://github.com/All-Hands-AI/OpenHands/blob/main/CONTRIBUTING.md) for specific instructions on how to contribute to OpenHands.
|
See the [CONTRIBUTING.md](https://github.com/All-Hands-AI/OpenHands/blob/main/CONTRIBUTING.md) for specific instructions on how to contribute to OpenHands.
|
||||||
|
|
||||||
### Global Microagents Best Practices
|
### Public Microagents Best Practices
|
||||||
|
|
||||||
- **Clear Scope**: Keep the microagent focused on a specific domain or task.
|
- **Clear Scope**: Keep the microagent focused on a specific domain or task.
|
||||||
- **Explicit Instructions**: Provide clear, unambiguous guidelines.
|
- **Explicit Instructions**: Provide clear, unambiguous guidelines.
|
||||||
@@ -18,11 +37,11 @@ See the [CONTRIBUTING.md](https://github.com/All-Hands-AI/OpenHands/blob/main/CO
|
|||||||
- **Safety First**: Include necessary warnings and constraints.
|
- **Safety First**: Include necessary warnings and constraints.
|
||||||
- **Integration Awareness**: Consider how the microagent interacts with other components.
|
- **Integration Awareness**: Consider how the microagent interacts with other components.
|
||||||
|
|
||||||
### Steps to Contribute a Global Microagent
|
### Steps to Contribute a Public Microagent
|
||||||
|
|
||||||
#### 1. Plan the Global Microagent
|
#### 1. Plan the Public Microagent
|
||||||
|
|
||||||
Before creating a global microagent, consider:
|
Before creating a public microagent, consider:
|
||||||
|
|
||||||
- What specific problem or use case will it address?
|
- What specific problem or use case will it address?
|
||||||
- What unique capabilities or knowledge should it have?
|
- What unique capabilities or knowledge should it have?
|
||||||
@@ -32,19 +51,23 @@ Before creating a global microagent, consider:
|
|||||||
#### 2. Create File
|
#### 2. Create File
|
||||||
|
|
||||||
Create a new Markdown file with a descriptive name in the appropriate directory:
|
Create a new Markdown file with a descriptive name in the appropriate directory:
|
||||||
[`microagents/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents)
|
|
||||||
|
|
||||||
#### 3. Testing the Global Microagent
|
- [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge) for knowledge microagents
|
||||||
|
- [`microagents/tasks/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/tasks) for task microagents
|
||||||
|
|
||||||
- Test the agent with various prompts.
|
Ensure it follows the correct [syntax](./microagents-syntax.md) and [best practices](./microagents-syntax.md#markdown-content-best-practices).
|
||||||
- Verify trigger words activate the agent correctly.
|
|
||||||
- Ensure instructions are clear and comprehensive.
|
#### 3. Testing the Public Microagent
|
||||||
- Check for potential conflicts and overlaps with existing agents.
|
|
||||||
|
- Test the agent with various prompts
|
||||||
|
- Verify trigger words activate the agent correctly
|
||||||
|
- Ensure instructions are clear and comprehensive
|
||||||
|
- Check for potential conflicts and overlaps with existing agents
|
||||||
|
|
||||||
#### 4. Submission Process
|
#### 4. Submission Process
|
||||||
|
|
||||||
Submit a pull request with:
|
Submit a pull request with:
|
||||||
|
|
||||||
- The new microagent file.
|
- The new microagent file
|
||||||
- Updated documentation if needed.
|
- Updated documentation if needed
|
||||||
- Description of the agent's purpose and capabilities.
|
- Description of the agent's purpose and capabilities
|
||||||
|
|||||||
@@ -1,38 +1,117 @@
|
|||||||
# General Repository Microagents
|
# Repository-specific Microagents
|
||||||
|
|
||||||
## Purpose
|
## Overview
|
||||||
|
|
||||||
General guidelines for OpenHands to work more effectively with the repository.
|
OpenHands can be customized to work more effectively with specific repositories by providing repository-specific context and guidelines.
|
||||||
|
|
||||||
## Microagent File
|
This section explains how to optimize OpenHands for your project.
|
||||||
|
|
||||||
Create a general repository microagent (example: `.openhands/microagents/repo.md`) to include
|
## Creating Repository Microagents
|
||||||
project-specific instructions, team practices, coding standards, and architectural guidelines that are relevant for
|
|
||||||
**all** prompts in that repository.
|
|
||||||
|
|
||||||
## Frontmatter Syntax
|
You can customize OpenHands' behavior for your repository by creating a `.openhands/microagents/` directory in your repository's root.
|
||||||
|
|
||||||
The frontmatter for this type of microagent is optional, unless you plan to include more than one general
|
You can enhance OpenHands' performance by adding custom microagents to your repository:
|
||||||
repository microagent.
|
|
||||||
|
|
||||||
Frontmatter should be enclosed in triple dashes (---) and may include the following fields:
|
1. For overall repository-specific instructions, create a `.openhands/microagents/repo.md` file
|
||||||
|
2. For reusable domain knowledge triggered by keywords, add multiple `.md` files to `.openhands/microagents/knowledge/`
|
||||||
|
3. For common workflows and tasks, create multiple `.md` files to `.openhands/microagents/tasks/`
|
||||||
|
|
||||||
| Field | Description | Required | Default |
|
Check out the [best practices](./microagents-syntax.md#markdown-content-best-practices) for formatting the content of your custom microagent.
|
||||||
|-----------|-----------------------------------------|--------------------------------------------------------------------|----------------|
|
|
||||||
| `name` | A unique identifier for the microagent | Required only if using more than one general repository microagent | 'default' |
|
|
||||||
| `agent` | The agent this microagent applies to | No | 'CodeActAgent' |
|
|
||||||
|
|
||||||
## Example
|
Keep in mind that loaded microagents take up space in the context window. It's crucial to strike a balance between the additional context provided by microagents and the instructions provided in the user's inputs.
|
||||||
|
|
||||||
|
Note that you can use OpenHands to create new microagents. The public microagent [`add_agent`](https://github.com/All-Hands-AI/OpenHands/blob/main/microagents/knowledge/add_agent.md) is loaded to all OpenHands instance and can support you on this.
|
||||||
|
|
||||||
|
## Types of Microagents
|
||||||
|
|
||||||
|
OpenHands supports three primary types of microagents, each with specific purposes and features to enhance agent performance:
|
||||||
|
|
||||||
|
- [repository](#repository-microagents)
|
||||||
|
- [knowledge](#knowledge-microagents)
|
||||||
|
- [tasks](#tasks-microagents)
|
||||||
|
|
||||||
|
The standard directory structure within a repository is:
|
||||||
|
|
||||||
|
- One main `repo.md` file containing repository-specific instructions
|
||||||
|
- Additional `Knowledge` agents in `.openhands/microagents/knowledge/` directory
|
||||||
|
- Additional `Task` agents in `.openhands/microagents/tasks/` directory
|
||||||
|
|
||||||
|
When processing the `.openhands/microagents/` directory, OpenHands will recursively scan all subfolders and process any `.md` files (except `README.md`) it finds. The system determines the microagent type based on the `type` field in the YAML frontmatter, not by the file's location. However, for organizational clarity, it's recommended to follow the standard directory structure.
|
||||||
|
|
||||||
|
### Repository Microagents
|
||||||
|
|
||||||
|
The `Repository` microagent is loaded specifically from `.openhands/microagents/repo.md` and serves as the main
|
||||||
|
repository-specific instruction file. This single file is automatically loaded whenever OpenHands works with that repository
|
||||||
|
without requiring any keyword matching or explicit call from the user.
|
||||||
|
|
||||||
|
OpenHands does not support multiple `repo.md` files in different locations or multiple microagents with type `repo`.
|
||||||
|
|
||||||
|
If you need to organize different types of repository information, the recommended approach is to use a single `repo.md` file with well-structured sections rather than trying to create multiple microagents with the type `repo`.
|
||||||
|
|
||||||
|
The best practice is to include project-specific instructions, team practices, coding standards, and architectural guidelines that are relevant for **all** prompts in that repository.
|
||||||
|
|
||||||
|
Example structure:
|
||||||
|
|
||||||
```
|
```
|
||||||
---
|
your-repository/
|
||||||
name: repo
|
└── .openhands/
|
||||||
---
|
└── microagents/
|
||||||
|
└── repo.md # Repository-specific instructions
|
||||||
This project is a TODO application that allows users to track TODO items.
|
|
||||||
|
|
||||||
To set it up, you can run `npm run build`.
|
|
||||||
Always make sure the tests are passing before committing changes. You can run the tests by running `npm run test`.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[See more examples of general repository microagents here.](https://github.com/All-Hands-AI/OpenHands/tree/main/.openhands/microagents)
|
[See the example in the official OpenHands repository](https://github.com/All-Hands-AI/OpenHands/blob/main/.openhands/microagents/repo.md?plain=1)
|
||||||
|
|
||||||
|
### Knowledge Microagents
|
||||||
|
|
||||||
|
Knowledge microagents provide specialized domain expertise:
|
||||||
|
|
||||||
|
- Recommended to be located in `.openhands/microagents/knowledge/`
|
||||||
|
- Triggered by specific keywords in conversations
|
||||||
|
- Contain expertise on tools, languages, frameworks, and common practices
|
||||||
|
|
||||||
|
Use knowledge microagents to trigger additional context relevant to specific technologies, tools, or workflows. For example, mentioning "git" in your conversation will automatically trigger git-related expertise to help with Git operations.
|
||||||
|
|
||||||
|
Examples structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
your-repository/
|
||||||
|
└── .openhands/
|
||||||
|
└── microagents/
|
||||||
|
└── knowledge/
|
||||||
|
└── git.md
|
||||||
|
└── docker.md
|
||||||
|
└── python.md
|
||||||
|
└── ...
|
||||||
|
└── repo.md
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find several real examples of `Knowledge` microagents in the [offical OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge)
|
||||||
|
|
||||||
|
### Tasks Microagents
|
||||||
|
|
||||||
|
Task microagents guide users through interactive workflows:
|
||||||
|
|
||||||
|
- Recommended to be located in `.openhands/microagents/tasks/`
|
||||||
|
- Provide step-by-step processes for common development tasks
|
||||||
|
- Accept inputs and adapt to different scenarios
|
||||||
|
- Ensure consistent outcomes for complex operations
|
||||||
|
|
||||||
|
Task microagents are a convenient way to store multi-step processes you perform regularly. For instance, you can create a `update_pr_description.md` microagent to automatically generate better pull request descriptions based on code changes.
|
||||||
|
|
||||||
|
Examples structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
your-repository/
|
||||||
|
└── .openhands/
|
||||||
|
└── microagents/
|
||||||
|
└── tasks/
|
||||||
|
└── update_pr_description.md
|
||||||
|
└── address_pr_comments.md
|
||||||
|
└── get_test_to_pass.md
|
||||||
|
└── ...
|
||||||
|
└── knowledge/
|
||||||
|
└── ...
|
||||||
|
└── repo.md
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find several real examples of `Tasks` microagents in the [offical OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/tasks)
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
# Microagents Syntax
|
||||||
|
|
||||||
|
Microagents are defined using markdown files with YAML frontmatter that specify their behavior, triggers, and capabilities.
|
||||||
|
|
||||||
|
Find below a comprehensive description of the frontmatter syntax and other details about how to use each type of microagent available at OpenHands.
|
||||||
|
|
||||||
|
## Frontmatter Schema
|
||||||
|
|
||||||
|
Every microagent requires a YAML frontmatter section at the beginning of the file, enclosed by triple dashes (`---`). The fields are:
|
||||||
|
|
||||||
|
| Field | Description | Required | Used By |
|
||||||
|
| ---------- | -------------------------------------------------- | ------------------------ | ---------------- |
|
||||||
|
| `name` | Unique identifier for the microagent | Yes | All types |
|
||||||
|
| `type` | Type of microagent: `repo`, `knowledge`, or `task` | Yes | All types |
|
||||||
|
| `version` | Version number (Semantic versioning recommended) | Yes | All types |
|
||||||
|
| `agent` | The agent type (typically `CodeActAgent`) | Yes | All types |
|
||||||
|
| `author` | Creator of the microagent | No | All types |
|
||||||
|
| `triggers` | List of keywords that activate the microagent | Yes for knowledge agents | Knowledge agents |
|
||||||
|
| `inputs` | Defines required user inputs for task execution | Yes for task agents | Task agents |
|
||||||
|
|
||||||
|
## Core Fields
|
||||||
|
|
||||||
|
### `agent`
|
||||||
|
|
||||||
|
**Purpose**: Specifies which agent implementation processes the microagent (typically `CodeActAgent`).
|
||||||
|
|
||||||
|
- Defines a single agent responsible for processing the microagent
|
||||||
|
- Must be available in the OpenHands system (see the [agent hub](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/agenthub))
|
||||||
|
- If the specified agent is not active, the microagent will not be used
|
||||||
|
|
||||||
|
### `triggers`
|
||||||
|
|
||||||
|
**Purpose**: Defines keywords that activate the `knowledge` microagent.
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
triggers:
|
||||||
|
- kubernetes
|
||||||
|
- k8s
|
||||||
|
- docker
|
||||||
|
- security
|
||||||
|
- containers cluster
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key points**:
|
||||||
|
|
||||||
|
- Can include both single words and multi-word phrases
|
||||||
|
- Case-insensitive matching is typically used
|
||||||
|
- More specific triggers (like "docker compose") prevent false activations
|
||||||
|
- Multiple triggers increase the chance of activation in relevant contexts
|
||||||
|
- Unique triggers like "flarglebargle" can be used for testing or special functionality
|
||||||
|
- Triggers should be carefully chosen to avoid unwanted activations or conflicts with other microagents
|
||||||
|
- Common terms used in many conversations may cause the microagent to be activated too frequently
|
||||||
|
|
||||||
|
When using multiple triggers, the microagent will be activated if any of the trigger words or phrases appear in the
|
||||||
|
conversation.
|
||||||
|
|
||||||
|
### `inputs`
|
||||||
|
|
||||||
|
**Purpose**: Defines parameters required from the user when a `task` microagent is activated.
|
||||||
|
|
||||||
|
**Schema**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
inputs:
|
||||||
|
- name: INPUT_NAME # Used with {{ INPUT_NAME }}
|
||||||
|
description: 'Description of what this input is for'
|
||||||
|
required: true # Optional, defaults to true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key points**:
|
||||||
|
|
||||||
|
- The `name` and `description` properties are required for each input
|
||||||
|
- The `required` property is optional and defaults to `true`
|
||||||
|
- Input values are referenced in the microagent body using double curly braces (e.g., `{{ INPUT_NAME }}`)
|
||||||
|
- All inputs defined will be collected from the user before the task microagent executes
|
||||||
|
|
||||||
|
**Variable Usage**: Reference input values using double curly braces `{{ INPUT_NAME }}`.
|
||||||
|
|
||||||
|
## Example Formats
|
||||||
|
|
||||||
|
### Repository Microagent
|
||||||
|
|
||||||
|
Repository microagents provide context and guidelines for a specific repository.
|
||||||
|
|
||||||
|
- Located at: `.openhands/microagents/repo.md`
|
||||||
|
- Automatically loaded when working with the repository
|
||||||
|
- Only one per repository
|
||||||
|
|
||||||
|
The `Repository` microagent is loaded specifically from `.openhands/microagents/repo.md` and serves as the main
|
||||||
|
repository-specific instruction file. This single file is automatically loaded whenever OpenHands works with that repository
|
||||||
|
without requiring any keyword matching or explicit call from the user.
|
||||||
|
|
||||||
|
[See the example in the official OpenHands repository](https://github.com/All-Hands-AI/OpenHands/blob/main/.openhands/microagents/repo.md?plain=1)
|
||||||
|
|
||||||
|
### Knowledge Microagent
|
||||||
|
|
||||||
|
Provides specialized domain expertise triggered by keywords.
|
||||||
|
|
||||||
|
You can find several real examples of `Knowledge` microagents in the [offical OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge)
|
||||||
|
|
||||||
|
### Task Microagent
|
||||||
|
|
||||||
|
When explicitly asked by the user, will guide through interactive workflows with specific inputs.
|
||||||
|
|
||||||
|
You can find several real examples of `Tasks` microagents in the [offical OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/tasks)
|
||||||
|
|
||||||
|
## Markdown Content Best Practices
|
||||||
|
|
||||||
|
After the frontmatter, compose the microagent body using Markdown syntax. Examples of elements you can include are:
|
||||||
|
|
||||||
|
- Clear, concise instructions outlining the microagent's purpose and responsibilities
|
||||||
|
- Specific guidelines and constraints the microagent should adhere to
|
||||||
|
- Relevant code snippets and practical examples to illustrate key points
|
||||||
|
- Step-by-step procedures for task agents, guiding users through workflows
|
||||||
|
|
||||||
|
**Design Tips**:
|
||||||
|
|
||||||
|
- Keep microagents focused with a clear purpose
|
||||||
|
- Provide specific guidelines rather than general advice
|
||||||
|
- Use distinctive triggers for knowledge agents
|
||||||
|
- Keep content concise to minimize context window usage
|
||||||
|
- Break large microagents into smaller, focused ones
|
||||||
|
|
||||||
|
Aim for clarity, brevity, and practicality in your writing. Use formatting like bullet points, code blocks, and emphasis to enhance readability and comprehension.
|
||||||
|
|
||||||
|
Remember that balancing microagents details with user input space is important for maintaining effective interactions.
|
||||||
Generated
+19
-414
@@ -24,8 +24,6 @@
|
|||||||
"@docusaurus/module-type-aliases": "^3.5.1",
|
"@docusaurus/module-type-aliases": "^3.5.1",
|
||||||
"@docusaurus/tsconfig": "^3.7.0",
|
"@docusaurus/tsconfig": "^3.7.0",
|
||||||
"@docusaurus/types": "^3.5.1",
|
"@docusaurus/types": "^3.5.1",
|
||||||
"swagger-cli": "^4.0.4",
|
|
||||||
"swagger-ui-dist": "^5.21.0",
|
|
||||||
"typescript": "~5.8.3"
|
"typescript": "~5.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -275,273 +273,6 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@apidevtools/openapi-schemas": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli": {
|
|
||||||
"version": "4.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.4.tgz",
|
|
||||||
"integrity": "sha512-hdDT3B6GLVovCsRZYDi3+wMcB1HfetTU20l2DC8zD3iFRNMC6QNAZG5fo/6PYeHWBEv7ri4MvnlKodhNB0nt7g==",
|
|
||||||
"deprecated": "This package has been abandoned. Please switch to using the actively maintained @redocly/cli",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@apidevtools/swagger-parser": "^10.0.1",
|
|
||||||
"chalk": "^4.1.0",
|
|
||||||
"js-yaml": "^3.14.0",
|
|
||||||
"yargs": "^15.4.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"swagger-cli": "bin/swagger-cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/argparse": {
|
|
||||||
"version": "1.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"sprintf-js": "~1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/camelcase": {
|
|
||||||
"version": "5.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
|
||||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/cliui": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"strip-ansi": "^6.0.0",
|
|
||||||
"wrap-ansi": "^6.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/find-up": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"locate-path": "^5.0.0",
|
|
||||||
"path-exists": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/js-yaml": {
|
|
||||||
"version": "3.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
|
||||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"argparse": "^1.0.7",
|
|
||||||
"esprima": "^4.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"js-yaml": "bin/js-yaml.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/locate-path": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"p-locate": "^4.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/p-limit": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"p-try": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/p-locate": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"p-limit": "^2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/path-exists": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/wrap-ansi": {
|
|
||||||
"version": "6.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
|
||||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-styles": "^4.0.0",
|
|
||||||
"string-width": "^4.1.0",
|
|
||||||
"strip-ansi": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/y18n": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/yargs": {
|
|
||||||
"version": "15.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
|
||||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cliui": "^6.0.0",
|
|
||||||
"decamelize": "^1.2.0",
|
|
||||||
"find-up": "^4.1.0",
|
|
||||||
"get-caller-file": "^2.0.1",
|
|
||||||
"require-directory": "^2.1.1",
|
|
||||||
"require-main-filename": "^2.0.0",
|
|
||||||
"set-blocking": "^2.0.0",
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"which-module": "^2.0.0",
|
|
||||||
"y18n": "^4.0.0",
|
|
||||||
"yargs-parser": "^18.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-cli/node_modules/yargs-parser": {
|
|
||||||
"version": "18.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
|
||||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"camelcase": "^5.0.0",
|
|
||||||
"decamelize": "^1.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-methods": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-parser": {
|
|
||||||
"version": "10.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.1.tgz",
|
|
||||||
"integrity": "sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@apidevtools/json-schema-ref-parser": "11.7.2",
|
|
||||||
"@apidevtools/openapi-schemas": "^2.1.0",
|
|
||||||
"@apidevtools/swagger-methods": "^3.0.2",
|
|
||||||
"@jsdevtools/ono": "^7.1.3",
|
|
||||||
"ajv": "^8.17.1",
|
|
||||||
"ajv-draft-04": "^1.0.0",
|
|
||||||
"call-me-maybe": "^1.0.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"openapi-types": ">=7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apidevtools/swagger-parser/node_modules/@apidevtools/json-schema-ref-parser": {
|
|
||||||
"version": "11.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz",
|
|
||||||
"integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jsdevtools/ono": "^7.1.3",
|
|
||||||
"@types/json-schema": "^7.0.15",
|
|
||||||
"js-yaml": "^4.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/philsturgeon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.26.2",
|
"version": "7.26.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||||
@@ -4104,13 +3835,6 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jsdevtools/ono": {
|
|
||||||
"version": "7.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
|
||||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@leichtgewicht/ip-codec": {
|
"node_modules/@leichtgewicht/ip-codec": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
||||||
@@ -4246,14 +3970,6 @@
|
|||||||
"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
|
"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@scarf/scarf": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@sideway/address": {
|
"node_modules/@sideway/address": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
||||||
@@ -5251,21 +4967,6 @@
|
|||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv-draft-04": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"ajv": "^8.5.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"ajv": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ajv-formats": {
|
"node_modules/ajv-formats": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||||
@@ -5848,13 +5549,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/call-me-maybe": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/callsites": {
|
"node_modules/callsites": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
@@ -7498,16 +7192,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/decamelize": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/decode-named-character-reference": {
|
"node_modules/decode-named-character-reference": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
||||||
@@ -8916,16 +8600,6 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-caller-file": {
|
|
||||||
"version": "2.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
|
||||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": "6.* || 8.* || >= 10.*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||||
@@ -13436,16 +13110,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.cjs"
|
||||||
},
|
},
|
||||||
@@ -13754,14 +13427,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/openapi-types": {
|
|
||||||
"version": "12.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
|
|
||||||
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/opener": {
|
"node_modules/opener": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||||
@@ -14144,9 +13809,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.49",
|
"version": "8.4.38",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -14161,11 +13826,10 @@
|
|||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.0.0",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
@@ -15703,15 +15367,6 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/punycode": {
|
|
||||||
"version": "2.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pupa": {
|
"node_modules/pupa": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz",
|
||||||
@@ -16550,16 +16205,6 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/require-directory": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/require-from-string": {
|
"node_modules/require-from-string": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
@@ -16577,13 +16222,6 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/require-main-filename": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/requires-port": {
|
"node_modules/requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
@@ -17064,13 +16702,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/set-blocking": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
@@ -17349,10 +16980,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -17717,32 +17347,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
|
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
|
||||||
},
|
},
|
||||||
"node_modules/swagger-cli": {
|
|
||||||
"version": "4.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/swagger-cli/-/swagger-cli-4.0.4.tgz",
|
|
||||||
"integrity": "sha512-Cp8YYuLny3RJFQ4CvOBTaqmOOgYsem52dPx1xM5S4EUWFblIh2Q8atppMZvXKUr1e9xH5RwipYpmdUzdPcxWcA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@apidevtools/swagger-cli": "4.0.4"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"swagger-cli": "swagger-cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/swagger-ui-dist": {
|
|
||||||
"version": "5.21.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz",
|
|
||||||
"integrity": "sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@scarf/scarf": "=1.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||||
@@ -18345,6 +17949,14 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uri-js/node_modules/punycode": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/url-loader": {
|
"node_modules/url-loader": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
|
||||||
@@ -18998,13 +18610,6 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/which-module": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/widest-line": {
|
"node_modules/widest-line": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
|
||||||
|
|||||||
+4
-9
@@ -4,18 +4,16 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docusaurus": "docusaurus",
|
"docusaurus": "docusaurus",
|
||||||
"start": "node generate-swagger-ui.js && docusaurus start",
|
"start": "docusaurus start",
|
||||||
"build": "node generate-swagger-ui.js && docusaurus build",
|
"build": "docusaurus build",
|
||||||
"swizzle": "docusaurus swizzle",
|
"swizzle": "docusaurus swizzle",
|
||||||
"deploy": "docusaurus deploy",
|
"deploy": "docusaurus deploy",
|
||||||
"clear": "docusaurus clear",
|
"clear": "docusaurus clear",
|
||||||
"serve": "docusaurus serve",
|
"serve": "docusaurus serve",
|
||||||
"write-translations": "docusaurus write-translations",
|
"write-translations": "docusaurus write-translations",
|
||||||
"write-heading-ids": "docusaurus write-heading-ids",
|
"write-heading-ids": "docusaurus write-heading-ids",
|
||||||
"typecheck": "tsc",
|
"typecheck": "tsc"
|
||||||
"generate-swagger-ui": "node generate-swagger-ui.js"
|
|
||||||
},
|
},
|
||||||
"// Note": "The OpenAPI spec is stored in docs/static/openapi.json so it's accessible at /openapi.json in the deployed site",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "^3.7.0",
|
"@docusaurus/core": "^3.7.0",
|
||||||
"@docusaurus/plugin-content-pages": "^3.7.0",
|
"@docusaurus/plugin-content-pages": "^3.7.0",
|
||||||
@@ -33,8 +31,6 @@
|
|||||||
"@docusaurus/module-type-aliases": "^3.5.1",
|
"@docusaurus/module-type-aliases": "^3.5.1",
|
||||||
"@docusaurus/tsconfig": "^3.7.0",
|
"@docusaurus/tsconfig": "^3.7.0",
|
||||||
"@docusaurus/types": "^3.5.1",
|
"@docusaurus/types": "^3.5.1",
|
||||||
"swagger-cli": "^4.0.4",
|
|
||||||
"swagger-ui-dist": "^5.21.0",
|
|
||||||
"typescript": "~5.8.3"
|
"typescript": "~5.8.3"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
@@ -51,6 +47,5 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0"
|
"node": ">=18.0"
|
||||||
},
|
}
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -66,18 +66,18 @@ const sidebars: SidebarsConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
label: 'General Repository Microagents',
|
label: 'Repository-specific',
|
||||||
id: 'usage/prompting/microagents-repo',
|
id: 'usage/prompting/microagents-repo',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
label: 'Keyword-Triggered Microagents',
|
label: 'Public',
|
||||||
id: 'usage/prompting/microagents-keyword',
|
id: 'usage/prompting/microagents-public',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
label: 'Global Microagents',
|
label: 'Syntax',
|
||||||
id: 'usage/prompting/microagents-public',
|
id: 'usage/prompting/microagents-syntax',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Vendored
-15
@@ -1,15 +0,0 @@
|
|||||||
# Static Files for OpenHands Documentation
|
|
||||||
|
|
||||||
This directory contains static files that are copied directly to the build output of the Docusaurus documentation.
|
|
||||||
|
|
||||||
## OpenAPI Specification
|
|
||||||
|
|
||||||
The `openapi.json` file in this directory is the OpenAPI specification for the OpenHands API. It is copied to the build output and is accessible at `/openapi.json` in the deployed site.
|
|
||||||
|
|
||||||
This file is used by the Swagger UI interface, which is accessible at `/swagger-ui/` in the deployed site.
|
|
||||||
|
|
||||||
## Why is the OpenAPI spec in the static directory?
|
|
||||||
|
|
||||||
The OpenAPI specification is placed in the static directory so that it's accessible at a predictable URL in the deployed site. This allows the Swagger UI to reference it directly.
|
|
||||||
|
|
||||||
We only need one copy of the OpenAPI spec file, which is this one in the static directory.
|
|
||||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 148 KiB |
Vendored
-2085
File diff suppressed because it is too large
Load Diff
+178
-450
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -74,6 +75,7 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -55,6 +56,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -61,6 +62,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
|
|
||||||
# copy 'draft_editor' config if exists
|
# copy 'draft_editor' config if exists
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -72,6 +73,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -86,6 +87,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -50,6 +51,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
update_llm_config_for_completions_logging,
|
update_llm_config_for_completions_logging,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
@@ -135,6 +136,7 @@ def get_config(
|
|||||||
enable_browsing=RUN_WITH_BROWSING,
|
enable_browsing=RUN_WITH_BROWSING,
|
||||||
enable_llm_editor=False,
|
enable_llm_editor=False,
|
||||||
)
|
)
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
config.set_agent_config(agent_config)
|
config.set_agent_config(agent_config)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -76,6 +77,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
agent_config = AgentConfig(
|
agent_config = AgentConfig(
|
||||||
function_calling=False,
|
function_calling=False,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -66,6 +67,8 @@ def get_config(
|
|||||||
else:
|
else:
|
||||||
logger.info('Agent config not provided, using default settings')
|
logger.info('Agent config not provided, using default settings')
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -54,6 +55,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -75,6 +76,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -96,6 +97,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
config.yaml
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# CI Builds Repair Benchmark Integration
|
|
||||||
|
|
||||||
This module integrates the CI Builds Repair benchmark developed by [JetBrains-Research](https://github.com/JetBrains-Research/lca-baselines/tree/main/ci-builds-repair/ci-builds-repair-benchmark).
|
|
||||||
|
|
||||||
For more information, refer to the [GitHub repository](https://github.com/JetBrains-Research/lca-baselines/tree/main/ci-builds-repair/ci-builds-repair-benchmark) and the associated [research paper](https://arxiv.org/abs/2406.11612).
|
|
||||||
See notice below for details
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Before running any scripts, make sure to configure the benchmark by setting up `config.yaml`.
|
|
||||||
This benchmark pushes to JetBrains' private GitHub repository. You will to request a `token_gh` provided by their team, to run this benchmark.
|
|
||||||
|
|
||||||
## Inference
|
|
||||||
|
|
||||||
To run inference with your model:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./evaluation/benchmarks/lca_ci_build_repair/scripts/run_infer.sh llm.yourmodel
|
|
||||||
```
|
|
||||||
|
|
||||||
## Evaluation
|
|
||||||
|
|
||||||
To evaluate the predictions:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./evaluation/benchmarks/lca_ci_build_repair/scripts/eval_infer.sh predictions_path_containing_output
|
|
||||||
```
|
|
||||||
|
|
||||||
## Results
|
|
||||||
The benchmark contains 68 instances, we skip instances #126 and #145, and only run 66 instances due to dockerization errors.
|
|
||||||
|
|
||||||
Due to running in live GitHub machines, the benchmark is sensitive to the date it is run. Even the golden patches in the dataset might present failures due to updates.
|
|
||||||
For example, on 2025-04-09, running the benchmark against the golden patches gave 57/67 successes, with 1 job left in the waiting list.
|
|
||||||
|
|
||||||
On 2025-04-10, running the benchmark full with OH and no oracle, 37 succeeded. That is 54% of the complete set of 68 instances and 64% of the 57 that succeed with golden patches.
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
LCA_PATH: path #where to clone lca-ci rep
|
|
||||||
model_name: OpenHands
|
|
||||||
benchmark_owner: ICML-25-BenchName-builds-repair
|
|
||||||
token_gh: your_token
|
|
||||||
#for lca-ci-repo
|
|
||||||
repos_folder: /path/to/repos # here the cloned repos would be stored
|
|
||||||
out_folder: /out/folder # here the result files would be stored
|
|
||||||
data_cache_dir: /data/cache/dir/ # here the cached dataset would be stored
|
|
||||||
username_gh: username-gh # your GitHub username
|
|
||||||
# test_username: test_user # username that would be displayed in the benchmark. Optional. If ommitted, username_gh would be used
|
|
||||||
language: Python # dataset language (now only Python is available)
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
"""Implements evaluation on JetBrains CI builds repair baselines
|
|
||||||
|
|
||||||
Please see https://github.com/JetBrains-Research/lca-baselines/tree/main/ci-builds-repair
|
|
||||||
and https://huggingface.co/datasets/JetBrains-Research/lca-ci-builds-repair
|
|
||||||
|
|
||||||
TODOs:
|
|
||||||
- Add more flags
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import ruamel.yaml
|
|
||||||
|
|
||||||
from evaluation.utils.shared import (
|
|
||||||
EvalMetadata,
|
|
||||||
get_default_sandbox_config_for_eval,
|
|
||||||
make_metadata,
|
|
||||||
)
|
|
||||||
from openhands.core.config import (
|
|
||||||
AppConfig,
|
|
||||||
LLMConfig,
|
|
||||||
get_parser,
|
|
||||||
load_app_config,
|
|
||||||
)
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
|
||||||
from openhands.core.main import create_runtime
|
|
||||||
from openhands.events.action import CmdRunAction
|
|
||||||
from openhands.events.observation import CmdOutputObservation
|
|
||||||
from openhands.runtime.base import Runtime
|
|
||||||
from openhands.utils.async_utils import call_async_from_sync
|
|
||||||
|
|
||||||
|
|
||||||
def get_config(
|
|
||||||
metadata: EvalMetadata,
|
|
||||||
) -> AppConfig:
|
|
||||||
sandbox_config = get_default_sandbox_config_for_eval()
|
|
||||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
|
||||||
config = AppConfig(
|
|
||||||
default_agent=metadata.agent_class,
|
|
||||||
run_as_openhands=False,
|
|
||||||
runtime='docker',
|
|
||||||
max_iterations=metadata.max_iterations,
|
|
||||||
sandbox=sandbox_config,
|
|
||||||
# do not mount workspace
|
|
||||||
workspace_base=None,
|
|
||||||
workspace_mount_path=None,
|
|
||||||
)
|
|
||||||
config.set_llm_config(metadata.llm_config)
|
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
|
||||||
agent_config.enable_prompt_extensions = False
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
config = load_app_config()
|
|
||||||
|
|
||||||
|
|
||||||
def load_bench_config():
|
|
||||||
script_dir = os.path.dirname(
|
|
||||||
os.path.abspath(__file__)
|
|
||||||
) # Get the absolute path of the script
|
|
||||||
config_path = os.path.join(script_dir, 'config.yaml')
|
|
||||||
yaml = ruamel.yaml.YAML(typ='rt')
|
|
||||||
with open(config_path, 'r') as file:
|
|
||||||
return yaml.load(file)
|
|
||||||
|
|
||||||
|
|
||||||
bench_config = load_bench_config()
|
|
||||||
|
|
||||||
|
|
||||||
def run_eval(
|
|
||||||
runtime: Runtime,
|
|
||||||
):
|
|
||||||
"""Run the evaluation and create report"""
|
|
||||||
logger.info(f"{'-' * 50} BEGIN Runtime Initialization Fn {'-' * 50}")
|
|
||||||
obs: CmdOutputObservation
|
|
||||||
|
|
||||||
lca_path = bench_config['LCA_PATH']
|
|
||||||
lca_ci_path = os.path.join(
|
|
||||||
lca_path, 'lca-baselines', 'ci-builds-repair', 'ci-builds-repair-benchmark'
|
|
||||||
)
|
|
||||||
|
|
||||||
model_name = bench_config['model_name']
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'mkdir {lca_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'cd {lca_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
lca_repo_url = 'https://github.com/juanmichelini/lca-baselines'
|
|
||||||
action = CmdRunAction(command=f'git clone {lca_repo_url}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'cd {lca_ci_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
action = CmdRunAction(command='git switch open-hands-integration')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(
|
|
||||||
os.path.abspath(__file__)
|
|
||||||
) # Get the absolute path of the script
|
|
||||||
config_path = os.path.join(script_dir, 'config.yaml')
|
|
||||||
runtime.copy_to(config_path, lca_ci_path)
|
|
||||||
|
|
||||||
token_gh = bench_config['token_gh']
|
|
||||||
commandf = f'export TOKEN_GH={token_gh}'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
action = CmdRunAction(command='poetry install')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
# Set up the task environment
|
|
||||||
commandf = f'poetry run python run_eval_jobs.py --model-name "{model_name}" --config-path "{lca_ci_path}/config.yaml" --job-ids-file "/tmp/output_lca.jsonl" --result-filename "testfile.jsonl" > /tmp/single_output.txt'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
logger.info(f'run_eval_jobs.py gave {obs.content} !')
|
|
||||||
# assert obs.exit_code == 0
|
|
||||||
|
|
||||||
commandf = 'cat /tmp/single_output.txt'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
logger.info(f' {commandf} gave {obs.content}!')
|
|
||||||
|
|
||||||
testfile_path = os.path.join(bench_config['out_folder'], 'testfile.jsonl')
|
|
||||||
commandf = f'cat {testfile_path}'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
report_str = obs.content
|
|
||||||
|
|
||||||
logger.info(f"{'-' * 50} END Runtime Initialization Fn {'-' * 50}")
|
|
||||||
return report_str
|
|
||||||
|
|
||||||
|
|
||||||
def process_predictions(predictions_path: str):
|
|
||||||
output_path = Path(predictions_path)
|
|
||||||
if output_path.suffix != '.jsonl':
|
|
||||||
raise ValueError('output_path must end in .jsonl')
|
|
||||||
|
|
||||||
output_lca_path = output_path.with_name(output_path.stem + '_lca.jsonl')
|
|
||||||
|
|
||||||
with output_path.open() as infile, output_lca_path.open('w') as outfile:
|
|
||||||
for line in infile:
|
|
||||||
data = json.loads(line)
|
|
||||||
json.dump(data.get('test_result'), outfile)
|
|
||||||
outfile.write('\n')
|
|
||||||
|
|
||||||
return str(output_lca_path)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = get_parser()
|
|
||||||
parser.add_argument(
|
|
||||||
'-s',
|
|
||||||
'--eval-split',
|
|
||||||
type=str,
|
|
||||||
default='test',
|
|
||||||
choices=['test'],
|
|
||||||
help='data split to evaluate on, must be test',
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--predictions-path',
|
|
||||||
type=str,
|
|
||||||
help='Path to the directory containing the output.jsonl with the predictions.',
|
|
||||||
)
|
|
||||||
args, _ = parser.parse_known_args()
|
|
||||||
|
|
||||||
data_split = args.eval_split
|
|
||||||
|
|
||||||
llm_config = LLMConfig(model='dummy_model')
|
|
||||||
|
|
||||||
metadata = make_metadata(
|
|
||||||
llm_config,
|
|
||||||
f'jetbrains-lca-ci--{data_split}',
|
|
||||||
args.agent_cls,
|
|
||||||
args.max_iterations,
|
|
||||||
args.eval_note,
|
|
||||||
args.predictions_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
# prepare image
|
|
||||||
config = get_config(metadata)
|
|
||||||
runtime = create_runtime(config)
|
|
||||||
call_async_from_sync(runtime.connect)
|
|
||||||
logger.info('Converting output.jsonl into output_lca.jsonl')
|
|
||||||
predictions_lca_path = process_predictions(
|
|
||||||
os.path.join(args.predictions_path, 'output.jsonl')
|
|
||||||
)
|
|
||||||
runtime.copy_to(predictions_lca_path, '/tmp')
|
|
||||||
|
|
||||||
# get results
|
|
||||||
results_str = run_eval(runtime)
|
|
||||||
results_path = os.path.join(args.predictions_path, 'results.jsonl')
|
|
||||||
with open(results_path, 'w') as file:
|
|
||||||
file.write(results_str)
|
|
||||||
logger.info(f'Saved results to {results_path}')
|
|
||||||
|
|
||||||
# make a summary
|
|
||||||
resolved_instances = []
|
|
||||||
unresolved_instances = []
|
|
||||||
for line in results_str.strip().splitlines():
|
|
||||||
data = json.loads(line)
|
|
||||||
conclusion = data.get('conclusion')
|
|
||||||
if conclusion == 'success':
|
|
||||||
resolved_instances.append(data)
|
|
||||||
elif conclusion == 'failure':
|
|
||||||
unresolved_instances.append(data)
|
|
||||||
|
|
||||||
completed_instances = resolved_instances + unresolved_instances
|
|
||||||
|
|
||||||
report = {
|
|
||||||
'success': len(resolved_instances),
|
|
||||||
'failure': len(unresolved_instances),
|
|
||||||
'resolved_instances': resolved_instances,
|
|
||||||
'unresolved_instances': unresolved_instances,
|
|
||||||
'completed_instances': completed_instances,
|
|
||||||
}
|
|
||||||
|
|
||||||
print(f'Results: {report}')
|
|
||||||
report_path = os.path.join(args.predictions_path, 'report.jsonl')
|
|
||||||
with open(report_path, 'w') as out_f:
|
|
||||||
out_f.write(json.dumps(report) + '\n')
|
|
||||||
|
|
||||||
logger.info(f'Saved report of results in swebench format to {report_path}')
|
|
||||||
@@ -1,406 +0,0 @@
|
|||||||
"""Implements inference on JetBrains CI builds repair baselines
|
|
||||||
|
|
||||||
Please see https://github.com/JetBrains-Research/lca-baselines/tree/main/ci-builds-repair
|
|
||||||
and https://huggingface.co/datasets/JetBrains-Research/lca-ci-builds-repair
|
|
||||||
|
|
||||||
TODOs:
|
|
||||||
- Add EXP_NAME
|
|
||||||
"""
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
import ruamel.yaml
|
|
||||||
from datasets import load_dataset
|
|
||||||
|
|
||||||
from evaluation.utils.shared import (
|
|
||||||
EvalMetadata,
|
|
||||||
EvalOutput,
|
|
||||||
codeact_user_response,
|
|
||||||
compatibility_for_eval_history_pairs,
|
|
||||||
get_default_sandbox_config_for_eval,
|
|
||||||
make_metadata,
|
|
||||||
prepare_dataset,
|
|
||||||
reset_logger_for_multiprocessing,
|
|
||||||
run_evaluation,
|
|
||||||
)
|
|
||||||
from openhands.controller.state.state import State
|
|
||||||
from openhands.core.config import (
|
|
||||||
AppConfig,
|
|
||||||
get_llm_config_arg,
|
|
||||||
get_parser,
|
|
||||||
load_app_config,
|
|
||||||
)
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
|
||||||
from openhands.core.main import create_runtime, run_controller
|
|
||||||
from openhands.events.action import CmdRunAction, MessageAction
|
|
||||||
from openhands.events.observation import CmdOutputObservation
|
|
||||||
from openhands.runtime.base import Runtime
|
|
||||||
from openhands.utils.async_utils import call_async_from_sync
|
|
||||||
|
|
||||||
|
|
||||||
def get_config(
|
|
||||||
metadata: EvalMetadata,
|
|
||||||
) -> AppConfig:
|
|
||||||
sandbox_config = get_default_sandbox_config_for_eval()
|
|
||||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
|
||||||
config = AppConfig(
|
|
||||||
default_agent=metadata.agent_class,
|
|
||||||
run_as_openhands=False,
|
|
||||||
runtime='docker',
|
|
||||||
max_iterations=metadata.max_iterations,
|
|
||||||
sandbox=sandbox_config,
|
|
||||||
# do not mount workspace
|
|
||||||
workspace_base=None,
|
|
||||||
workspace_mount_path=None,
|
|
||||||
)
|
|
||||||
config.set_llm_config(metadata.llm_config)
|
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
|
||||||
agent_config.enable_prompt_extensions = False
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
config = load_app_config()
|
|
||||||
|
|
||||||
|
|
||||||
def load_bench_config():
|
|
||||||
script_dir = os.path.dirname(
|
|
||||||
os.path.abspath(__file__)
|
|
||||||
) # Get the absolute path of the script
|
|
||||||
config_path = os.path.join(script_dir, 'config.yaml')
|
|
||||||
yaml = ruamel.yaml.YAML(typ='rt')
|
|
||||||
with open(config_path, 'r') as file:
|
|
||||||
return yaml.load(file)
|
|
||||||
|
|
||||||
|
|
||||||
bench_config = load_bench_config()
|
|
||||||
|
|
||||||
AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
|
|
||||||
'CodeActAgent': codeact_user_response,
|
|
||||||
}
|
|
||||||
|
|
||||||
AGENT_CLS_TO_INST_SUFFIX = {
|
|
||||||
'CodeActAgent': 'When you think you have completed the task, please finish the interaction using the "finish" tool.\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_runtime(
|
|
||||||
runtime: Runtime,
|
|
||||||
instance: pd.Series,
|
|
||||||
):
|
|
||||||
"""Initialize the runtime for the agent.
|
|
||||||
|
|
||||||
This function is called before the runtime is used to run the agent.
|
|
||||||
"""
|
|
||||||
logger.info(f"{'-' * 50} BEGIN Runtime Initialization Fn {'-' * 50}")
|
|
||||||
obs: CmdOutputObservation
|
|
||||||
|
|
||||||
lca_path = bench_config['LCA_PATH']
|
|
||||||
lca_ci_path = os.path.join(
|
|
||||||
lca_path, 'lca-baselines', 'ci-builds-repair', 'ci-builds-repair-benchmark'
|
|
||||||
)
|
|
||||||
|
|
||||||
repo_name = instance['repo_name']
|
|
||||||
repos_path = bench_config['repos_folder']
|
|
||||||
repo_owner = instance['repo_owner']
|
|
||||||
repo_path = os.path.join(repos_path, f'{repo_owner}__{repo_name}')
|
|
||||||
model_name = bench_config['model_name']
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'mkdir {lca_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'cd {lca_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
lca_repo_url = 'https://github.com/juanmichelini/lca-baselines'
|
|
||||||
action = CmdRunAction(command=f'git clone {lca_repo_url}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'cd {lca_ci_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
action = CmdRunAction(command='git switch open-hands-integration')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(
|
|
||||||
os.path.abspath(__file__)
|
|
||||||
) # Get the absolute path of the script
|
|
||||||
config_path = os.path.join(script_dir, 'config.yaml')
|
|
||||||
with open(config_path, 'r') as file:
|
|
||||||
config_as_text = file.read()
|
|
||||||
|
|
||||||
commandf = f"echo '{config_as_text}' > config.yaml"
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
token_gh = bench_config['token_gh']
|
|
||||||
commandf = f'export TOKEN_GH={token_gh}'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
action = CmdRunAction(command='poetry install')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
# Set up the task environment
|
|
||||||
commandf = f'poetry run python run_get_datapoint.py --model-name {model_name} --id {instance["id"]} > branch_name.txt'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
if obs.exit_code != 0:
|
|
||||||
print(f'run_get_datapoint.py failed at {instance["id"]} with {obs.content}')
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
commandf = 'cat branch_name.txt'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
bench_config['user_branch_name'] = obs.content
|
|
||||||
|
|
||||||
# Navigate to the task's code path
|
|
||||||
action = CmdRunAction(command=f'cd {repo_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
logger.info(f"{'-' * 50} END Runtime Initialization Fn {'-' * 50}")
|
|
||||||
|
|
||||||
|
|
||||||
def complete_runtime(
|
|
||||||
runtime: Runtime,
|
|
||||||
instance: pd.Series,
|
|
||||||
) -> dict[str, Any]:
|
|
||||||
"""Complete the runtime for the agent.
|
|
||||||
|
|
||||||
This function is called before the runtime is used to run the agent.
|
|
||||||
If you need to do something in the sandbox to get the correctness metric after
|
|
||||||
the agent has run, modify this function.
|
|
||||||
"""
|
|
||||||
logger.info(f"{'-' * 50} BEGIN Runtime Completion Fn {'-' * 50}")
|
|
||||||
obs: CmdOutputObservation
|
|
||||||
|
|
||||||
model_name = bench_config['model_name']
|
|
||||||
|
|
||||||
lca_path = bench_config['LCA_PATH']
|
|
||||||
lca_ci_path = os.path.join(
|
|
||||||
lca_path, 'lca-baselines', 'ci-builds-repair', 'ci-builds-repair-benchmark'
|
|
||||||
)
|
|
||||||
|
|
||||||
user_branch_name = bench_config['user_branch_name']
|
|
||||||
|
|
||||||
token_gh = bench_config['token_gh']
|
|
||||||
commandf = f'export TOKEN_GH={token_gh}'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
|
|
||||||
# Navigate to the lca-baseslines scripts path
|
|
||||||
action = CmdRunAction(command=f'cd {lca_ci_path}')
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
assert obs.exit_code == 0
|
|
||||||
|
|
||||||
commandf = f'poetry run python run_push_datapoint.py --id {instance["id"]} --model-name {model_name} --user-branch-name {user_branch_name} > single_output.json'
|
|
||||||
logger.info(f'Running push script: {commandf}')
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
# assert obs.exit_code == 0
|
|
||||||
|
|
||||||
commandf = 'cat single_output.json'
|
|
||||||
action = CmdRunAction(command=commandf)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
result = json.loads(obs.content)
|
|
||||||
|
|
||||||
logger.info(f"{'-' * 50} END Runtime Completion Fn {'-' * 50}")
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def process_instance(instance: Any, metadata: EvalMetadata, reset_logger: bool = True):
|
|
||||||
config = get_config(metadata)
|
|
||||||
|
|
||||||
# Setup the logger properly, so you can run multi-processing to parallelize the evaluation
|
|
||||||
if reset_logger:
|
|
||||||
log_dir = os.path.join(metadata.eval_output_dir, 'infer_logs')
|
|
||||||
reset_logger_for_multiprocessing(logger, instance['instance_id'], log_dir)
|
|
||||||
else:
|
|
||||||
logger.info(f'Starting evaluation for instance {instance["instance_id"]}.')
|
|
||||||
|
|
||||||
repo_name = instance['repo_name']
|
|
||||||
repo_workflow = instance['workflow_path']
|
|
||||||
repo_logs = instance['logs']
|
|
||||||
repos_path = bench_config['repos_folder']
|
|
||||||
repo_owner = instance['repo_owner']
|
|
||||||
repo_path = os.path.join(repos_path, f'{repo_owner}__{repo_name}')
|
|
||||||
|
|
||||||
# Prepare the task instruction
|
|
||||||
instruction_no_oracle = f"""
|
|
||||||
<uploaded_files>
|
|
||||||
{repo_path}
|
|
||||||
</uploaded_files>
|
|
||||||
|
|
||||||
I've uploaded a python code repository in the directory {repo_path}, Consider the following issue:
|
|
||||||
|
|
||||||
<issue_description>
|
|
||||||
The repository must pass the CI workflow {repo_workflow}.
|
|
||||||
but it gave the following error
|
|
||||||
{repo_logs}
|
|
||||||
</issue_description>
|
|
||||||
|
|
||||||
Can you help me implement the necessary changes to the repository so that the requirements specified in the <issue_description> are met?
|
|
||||||
I've already taken care of all changes to any of the test files described in the <issue_description>. This means you DON'T have to modify the testing logic or any of the tests in any way!
|
|
||||||
Also the development Python environment is already set up for you (i.e., all dependencies already installed), so you don't need to install other packages.
|
|
||||||
Your task is to make the minimal changes to non-test files in the {repo_path} directory to ensure the <issue_description> is satisfied.
|
|
||||||
|
|
||||||
Follow these phases to resolve the issue:
|
|
||||||
|
|
||||||
Phase 1. READING: read the problem and reword it in clearer terms
|
|
||||||
1.1 If there are code or config snippets. Express in words any best practices or conventions in them.
|
|
||||||
1.2 Hightlight message errors, method names, variables, file names, stack traces, and technical details.
|
|
||||||
1.3 Explain the problem in clear terms.
|
|
||||||
1.4 Enumerate the steps to reproduce the problem.
|
|
||||||
1.5 Hightlight any best practices to take into account when testing and fixing the issue
|
|
||||||
|
|
||||||
Phase 2. RUNNING: install and run the tests on the repository
|
|
||||||
2.1 Follow the readme
|
|
||||||
2.2 Install the environment and anything needed
|
|
||||||
2.2 Iterate and figure out how to run the tests
|
|
||||||
|
|
||||||
Phase 3. EXPLORATION: find the files that are related to the problem and possible solutions
|
|
||||||
3.1 Use `grep` to search for relevant methods, classes, keywords and error messages.
|
|
||||||
3.2 Identify all files related to the problem statement.
|
|
||||||
3.3 Propose the methods and files to fix the issue and explain why.
|
|
||||||
3.4 From the possible file locations, select the most likely location to fix the issue.
|
|
||||||
|
|
||||||
Phase 4. TEST CREATION: before implementing any fix, create a script to reproduce and verify the issue.
|
|
||||||
4.1 Look at existing test files in the repository to understand the test format/structure.
|
|
||||||
4.2 Create a minimal reproduction script that reproduces the located issue.
|
|
||||||
4.3 Run the reproduction script to confirm you are reproducing the issue.
|
|
||||||
4.4 Adjust the reproduction script as necessary.
|
|
||||||
|
|
||||||
Phase 5. FIX ANALYSIS: state clearly the problem and how to fix it
|
|
||||||
5.1 State clearly what the problem is.
|
|
||||||
5.2 State clearly where the problem is located.
|
|
||||||
5.3 State clearly how the test reproduces the issue.
|
|
||||||
5.4 State clearly the best practices to take into account in the fix.
|
|
||||||
5.5 State clearly how to fix the problem.
|
|
||||||
|
|
||||||
Phase 6. FIX IMPLEMENTATION: Edit the source code to implement your chosen solution.
|
|
||||||
6.1 Make minimal, focused changes to fix the issue.
|
|
||||||
|
|
||||||
Phase 7. VERIFICATION: Test your implementation thoroughly.
|
|
||||||
7.1 Run your reproduction script to verify the fix works.
|
|
||||||
7.2 Add edge cases to your test script to ensure comprehensive coverage.
|
|
||||||
7.3 Run existing tests related to the modified code to ensure you haven't broken anything. Run any tests in the repository related to:
|
|
||||||
7.2.1 The issue you are fixing
|
|
||||||
7.2.2 The files you modified
|
|
||||||
7.2.3 The functions you changed
|
|
||||||
7.4 If any tests fail, revise your implementation until all tests pass
|
|
||||||
|
|
||||||
Phase 8. REVIEW: Carefully re-read the problem description and compare your changes with the base commit {instance["sha_fail"]}.
|
|
||||||
8.1 Ensure you've fully addressed all requirements.
|
|
||||||
|
|
||||||
Once all phases are done, announce: 'Agent Task Complete'.
|
|
||||||
Be thorough in your exploration, testing, and reasoning. It's fine if your thinking process is lengthy - quality and completeness are more important than brevity.
|
|
||||||
"""
|
|
||||||
runtime = create_runtime(config)
|
|
||||||
call_async_from_sync(runtime.connect)
|
|
||||||
initialize_runtime(runtime, instance)
|
|
||||||
|
|
||||||
# Run the agent
|
|
||||||
state: State | None = asyncio.run(
|
|
||||||
run_controller(
|
|
||||||
config=config,
|
|
||||||
initial_user_action=MessageAction(content=instruction_no_oracle),
|
|
||||||
runtime=runtime,
|
|
||||||
fake_user_response_fn=AGENT_CLS_TO_FAKE_USER_RESPONSE_FN.get(
|
|
||||||
metadata.agent_class
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert state is not None
|
|
||||||
metrics = state.metrics.get() if state.metrics else {}
|
|
||||||
|
|
||||||
test_result = complete_runtime(runtime, instance)
|
|
||||||
|
|
||||||
# history is now available as a stream of events, rather than list of pairs of (Action, Observation)
|
|
||||||
# for compatibility with the existing output format, we can remake the pairs here
|
|
||||||
# remove when it becomes unnecessary
|
|
||||||
histories = compatibility_for_eval_history_pairs(state.history)
|
|
||||||
|
|
||||||
# Save the output
|
|
||||||
output = EvalOutput(
|
|
||||||
instance_id=instance['instance_id'],
|
|
||||||
# instance=instance.to_dict(orient='recorods'),
|
|
||||||
instruction=instruction_no_oracle,
|
|
||||||
metadata=metadata,
|
|
||||||
history=histories,
|
|
||||||
test_result=test_result,
|
|
||||||
metrics=metrics,
|
|
||||||
)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = get_parser()
|
|
||||||
parser.add_argument(
|
|
||||||
'-s',
|
|
||||||
'--eval-split',
|
|
||||||
type=str,
|
|
||||||
default='test',
|
|
||||||
choices=['test'],
|
|
||||||
help='data split to evaluate on, must be test',
|
|
||||||
)
|
|
||||||
args, _ = parser.parse_known_args()
|
|
||||||
|
|
||||||
data_split = args.eval_split
|
|
||||||
|
|
||||||
bench = load_dataset(
|
|
||||||
'JetBrains-Research/lca-ci-builds-repair', split=data_split
|
|
||||||
).to_pandas()
|
|
||||||
# todo: see why 126 is giving problems on inference
|
|
||||||
# todo: see why 145 is giving problems on eval
|
|
||||||
bench = bench[bench['id'] != 126]
|
|
||||||
bench = bench[bench['id'] != 145]
|
|
||||||
# bench = bench.iloc[0:56]
|
|
||||||
# add column instnace_id for compatibility with oh repo, old id column must be kept for lca repo
|
|
||||||
bench['instance_id'] = bench['id'].astype(str)
|
|
||||||
|
|
||||||
llm_config = None
|
|
||||||
if args.llm_config:
|
|
||||||
llm_config = get_llm_config_arg(args.llm_config)
|
|
||||||
# modify_params must be False for evaluation purpose, for reproducibility and accurancy of results
|
|
||||||
llm_config.modify_params = False
|
|
||||||
if llm_config is None:
|
|
||||||
raise ValueError(f'Could not find LLM config: --llm_config {args.llm_config}')
|
|
||||||
|
|
||||||
metadata = make_metadata(
|
|
||||||
llm_config,
|
|
||||||
f'jetbrains-lca-ci--{data_split}',
|
|
||||||
args.agent_cls,
|
|
||||||
args.max_iterations,
|
|
||||||
args.eval_note,
|
|
||||||
args.eval_output_dir,
|
|
||||||
)
|
|
||||||
output_file = os.path.join(metadata.eval_output_dir, 'output.jsonl')
|
|
||||||
instances = prepare_dataset(bench, output_file, args.eval_n_limit)
|
|
||||||
|
|
||||||
run_evaluation(
|
|
||||||
instances, metadata, output_file, args.eval_num_workers, process_instance
|
|
||||||
)
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
source "evaluation/utils/version_control.sh"
|
|
||||||
|
|
||||||
PROCESS_FILEPATH=$1
|
|
||||||
if [ -z "$PROCESS_FILEPATH" ]; then
|
|
||||||
echo "Error: PROCESS_FILEPATH is empty. Usage: ./eval_infer.sh <output_file> [instance_id] [dataset_name] [split]"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
get_openhands_version
|
|
||||||
|
|
||||||
PROCESS_FILEPATH=$(realpath $PROCESS_FILEPATH)
|
|
||||||
echo "OPENHANDS_VERSION: $OPENHANDS_VERSION"
|
|
||||||
echo "PROCESS_FILEPATH: $PROCESS_FILEPATH"
|
|
||||||
|
|
||||||
EVAL_NOTE="$OPENHANDS_VERSION"
|
|
||||||
if [ -n "$EXP_NAME" ]; then
|
|
||||||
EVAL_NOTE="$EVAL_NOTE-$EXP_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
function run_eval() {
|
|
||||||
COMMAND="poetry run python ./evaluation/benchmarks/lca_ci_build_repair/eval_infer.py \
|
|
||||||
--predictions-path $PROCESS_FILEPATH "
|
|
||||||
|
|
||||||
echo "RUNNING: $COMMAND"
|
|
||||||
# Run the command
|
|
||||||
eval $COMMAND
|
|
||||||
}
|
|
||||||
|
|
||||||
unset SANDBOX_ENV_GITHUB_TOKEN # prevent the agent from using the github token to push
|
|
||||||
run_eval
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
source "evaluation/utils/version_control.sh"
|
|
||||||
|
|
||||||
MODEL_CONFIG=$1
|
|
||||||
|
|
||||||
get_openhands_version
|
|
||||||
|
|
||||||
echo "OPENHANDS_VERSION: $OPENHANDS_VERSION"
|
|
||||||
echo "MODEL_CONFIG: $MODEL_CONFIG"
|
|
||||||
|
|
||||||
EVAL_NOTE="$OPENHANDS_VERSION"
|
|
||||||
if [ -n "$EXP_NAME" ]; then
|
|
||||||
EVAL_NOTE="$EVAL_NOTE-$EXP_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
function run_eval() {
|
|
||||||
COMMAND="poetry run python ./evaluation/benchmarks/lca_ci_build_repair/run_infer.py \
|
|
||||||
--llm-config $MODEL_CONFIG "
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
eval $COMMAND
|
|
||||||
}
|
|
||||||
|
|
||||||
#unset SANDBOX_ENV_GITHUB_TOKEN # prevent the agent from using the github token to push
|
|
||||||
run_eval
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
"""Installs LCA CI Build Repair benchmark with scripts for OH integration."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
|
|
||||||
def setup():
|
|
||||||
# Read config.yaml
|
|
||||||
print('Reading config.yaml')
|
|
||||||
script_dir = os.path.dirname(
|
|
||||||
os.path.abspath(__file__)
|
|
||||||
) # Get the absolute path of the script
|
|
||||||
config_path = os.path.join(script_dir, 'config.yaml')
|
|
||||||
with open(config_path, 'r') as f:
|
|
||||||
config = yaml.safe_load(f)
|
|
||||||
|
|
||||||
lca_path = config['LCA_PATH']
|
|
||||||
lca_ci_path = os.path.join(
|
|
||||||
lca_path, 'lca-baselines', 'ci-builds-repair', 'ci-builds-repair-benchmark'
|
|
||||||
)
|
|
||||||
repo_url = 'https://github.com/juanmichelini/lca-baselines'
|
|
||||||
|
|
||||||
# Clone the repository to LCA_CI_PATH
|
|
||||||
print(f'Cloning lca-baselines repository from {repo_url} into {lca_path}')
|
|
||||||
result = subprocess.run(
|
|
||||||
['git', 'clone', repo_url], cwd=lca_path, capture_output=True, text=True
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
print(f'Warning cloning repository: {result.stderr}')
|
|
||||||
|
|
||||||
# Clone the repository to LCA_CI_PATH
|
|
||||||
print('Switching branches')
|
|
||||||
result = subprocess.run(
|
|
||||||
['git', 'switch', 'open-hands-integration'],
|
|
||||||
cwd=lca_ci_path,
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
print(f'Warning switching repository: {result.stderr}')
|
|
||||||
|
|
||||||
# Move and rename config_lca.yaml (overwrite if exists)
|
|
||||||
lca_ci_config_path = os.path.join(lca_ci_path, 'config.yaml')
|
|
||||||
print(f'Copying config.yaml to {lca_ci_config_path}')
|
|
||||||
shutil.copy(config_path, lca_ci_config_path)
|
|
||||||
|
|
||||||
# Run poetry install in LCA_CI_PATH
|
|
||||||
print(f"Running 'poetry install' in {lca_ci_path}")
|
|
||||||
result = subprocess.run(
|
|
||||||
['poetry', 'install'], cwd=lca_ci_path, capture_output=True, text=True
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
print(f'Warning during poetry install: {result.stderr}')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
setup()
|
|
||||||
@@ -14,6 +14,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -63,6 +64,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -121,6 +122,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -91,6 +92,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
This folder contains the evaluation harness that we built on top of the original [SWE-Bench benchmark](https://www.swebench.com/) ([paper](https://arxiv.org/abs/2310.06770)).
|
This folder contains the evaluation harness that we built on top of the original [SWE-Bench benchmark](https://www.swebench.com/) ([paper](https://arxiv.org/abs/2310.06770)).
|
||||||
|
|
||||||
**UPDATE (4/8/2025): We now support running SWT-Bench evaluation! For more details, checkout [the corresponding section](#SWT-Bench-Evaluation).**
|
|
||||||
|
|
||||||
**UPDATE (03/27/2025): We now support SWE-Bench multimodal evaluation! Simply use "princeton-nlp/SWE-bench_Multimodal" as the dataset name in the `run_infer.sh` script to evaluate on multimodal instances.**
|
**UPDATE (03/27/2025): We now support SWE-Bench multimodal evaluation! Simply use "princeton-nlp/SWE-bench_Multimodal" as the dataset name in the `run_infer.sh` script to evaluate on multimodal instances.**
|
||||||
|
|
||||||
**UPDATE (2/18/2025): We now support running SWE-Gym using the same evaluation harness here. For more details, checkout [this README](./SWE-Gym.md).**
|
**UPDATE (2/18/2025): We now support running SWE-Gym using the same evaluation harness here. For more details, checkout [this README](./SWE-Gym.md).**
|
||||||
@@ -143,7 +141,7 @@ With `output.jsonl` file, you can run `eval_infer.sh` to evaluate generated patc
|
|||||||
./evaluation/benchmarks/swe_bench/scripts/eval_infer.sh $YOUR_OUTPUT_JSONL [instance_id] [dataset_name] [split]
|
./evaluation/benchmarks/swe_bench/scripts/eval_infer.sh $YOUR_OUTPUT_JSONL [instance_id] [dataset_name] [split]
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
./evaluation/benchmarks/swe_bench/scripts/eval_infer.sh evaluation/evaluation_outputs/outputs/princeton-nlp__SWE-bench_Lite/CodeActAgent/gpt-4-1106-preview_maxiter_50_N_v1.0/output.jsonl
|
./evaluation/benchmarks/swe_bench/scripts/eval_infer.sh evaluation/evaluation_outputs/outputs/swe_bench/CodeActAgent/gpt-4-1106-preview_maxiter_50_N_v1.0/output.jsonl
|
||||||
```
|
```
|
||||||
|
|
||||||
The script now accepts optional arguments:
|
The script now accepts optional arguments:
|
||||||
@@ -184,58 +182,3 @@ To clean-up all existing runtimes that you've already started, run:
|
|||||||
```bash
|
```bash
|
||||||
ALLHANDS_API_KEY="YOUR-API-KEY" ./evaluation/utils/scripts/cleanup_remote_runtime.sh
|
ALLHANDS_API_KEY="YOUR-API-KEY" ./evaluation/utils/scripts/cleanup_remote_runtime.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## SWT-Bench Evaluation
|
|
||||||
|
|
||||||
[SWT-Bench](https://swtbench.com/) ([paper](https://arxiv.org/abs/2406.12952)) is a benchmark for evaluating the capability of LLMs at creating unit tests. It is performed on the same instances as SWE-Bench, but requires a separate evaluation harness to capture coverage and issue reproduction. We therefore detail below how to leverage the inference script in this folder to run inference on SWT-Bench and how to use the SWT-Bench evaluation harness to evaluate them.
|
|
||||||
|
|
||||||
### Run inference on SWT-Bench
|
|
||||||
|
|
||||||
To run inference on SWT-Bench, you can use the same `run_infer.sh` script as described for evaluation on plain SWE-Bench. The only differences is that you need to specify the `mode` parameter to `swt` or `swt-ci` when running the script. For example, to run inference on SWT-Bench Verified, run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./evaluation/benchmarks/swe_bench/scripts/run_infer.sh [model_config] [git-version] [agent] [eval_limit] [max_iter] [num_workers] [swe-dataset] test 1 swt
|
|
||||||
|
|
||||||
# Example - This runs evaluation on CodeActAgent for 500 instances on "SWT-bench_Verified"'s test set (corresponding to SWE-bench_Verified), with max 100 iteration per instances, with 1 number of workers running in parallel
|
|
||||||
./evaluation/benchmarks/swe_bench/scripts/run_infer.sh llm.eval_gpt4o-2024-11-20 HEAD CodeActAgent 500 100 1 princeton-nlp/SWE-bench_Verified test 1 swt
|
|
||||||
```
|
|
||||||
|
|
||||||
The two modes `swt` and `swt-ci` have the following effect:
|
|
||||||
- `swt`: This mode will change the prompt to instruct the agent to generate reproducing test cases instead of resolving the issue.
|
|
||||||
- `swt-ci`: In addition to the changes by `swt`, this mode sets up the CI environment by i) pre-installing the environment in the docker image, such that the test framework can be executed without errors and ii) telling the model the exact command to run the test framework.
|
|
||||||
|
|
||||||
### Run evaluation for SWT-bench
|
|
||||||
|
|
||||||
The evaluation of these results is done leveraging [the SWT-Bench evaluation harness](https://github.com/logic-star-ai/swt-bench/tree/master).
|
|
||||||
|
|
||||||
#### Extracting results into SWT-Bench harness format
|
|
||||||
In order to run evaluation of the obtained inference results in the SWT-Bench harness, we transform the results to a format that the SWT-Bench evaluation harness expects.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 evaluation/benchmarks/swe_bench/scripts/swtbench/convert.py --prediction_file [output.jsonl] > [output_swt.jsonl]
|
|
||||||
|
|
||||||
# Example
|
|
||||||
python3 evaluation/benchmarks/swe_bench/scripts/swtbench/convert.py --prediction_file "evaluation/evaluation_outputs/outputs/princeton-nlp__SWE-bench_Verified-test/CodeActAgent/gpt-4o-2024-11-20_maxiter_100_N_v0.31.0-no-hint-swt-run_1/output.jsonl" > OpenHands-gpt-4o-2024-11-20.jsonl
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Running the results in SWT-Bench
|
|
||||||
|
|
||||||
Next, we run the [SWT-Bench evaluation harness](https://github.com/logic-star-ai/swt-bench/tree/master) with these results.
|
|
||||||
First set-up and validate the setup as described in the harness [here](https://github.com/logic-star-ai/swt-bench/tree/master?tab=readme-ov-file#-set-up).
|
|
||||||
Then, run the evaluation with the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Example
|
|
||||||
python3 -m src.main \
|
|
||||||
--dataset_name princeton-nlp/SWE-bench_Verified \
|
|
||||||
--predictions_path <pathTo>/OpenHands-gpt-4o-2024-11-20.jsonl \
|
|
||||||
--max_workers 12 \
|
|
||||||
--run_id OpenHands-CodeAct-gpt-4o-2024-11-20 --patch_types vanilla --build_mode api
|
|
||||||
```
|
|
||||||
|
|
||||||
The results of the evaluation can be obtained by running the reporting script of the harness.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Example
|
|
||||||
python -m src.report run_instance_swt_logs/OpenHands-CodeAct-gpt-4o-2024-11-20/OpenHands__CodeActAgent__gpt-4o-2024-11-20 --dataset verified
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,842 +0,0 @@
|
|||||||
# Based on https://github.com/logic-star-ai/swt-bench/blob/master/src/constants.py
|
|
||||||
|
|
||||||
# Constants - Installation Specifications
|
|
||||||
MAP_VERSION_TO_INSTALL_SKLEARN = {
|
|
||||||
k: {
|
|
||||||
'python': '3.6',
|
|
||||||
'packages': 'numpy scipy cython pytest pandas matplotlib',
|
|
||||||
'install': 'python -m pip install -v --no-use-pep517 --no-build-isolation -e .',
|
|
||||||
'pip_packages': [
|
|
||||||
'cython',
|
|
||||||
'numpy==1.19.2',
|
|
||||||
'setuptools',
|
|
||||||
'scipy==1.5.2',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['0.20', '0.21', '0.22']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_SKLEARN.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': "'numpy==1.19.2' 'scipy==1.5.2' 'cython==3.0.10' pytest 'pandas<2.0.0' 'matplotlib<3.9.0' setuptools pytest joblib threadpoolctl",
|
|
||||||
'install': 'python -m pip install -v --no-use-pep517 --no-build-isolation -e .',
|
|
||||||
'pip_packages': ['cython', 'setuptools', 'numpy', 'scipy'],
|
|
||||||
}
|
|
||||||
for k in ['1.3', '1.4']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_FLASK = {
|
|
||||||
'2.0': {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': [
|
|
||||||
'setuptools==70.0.0',
|
|
||||||
'Werkzeug==2.3.7',
|
|
||||||
'Jinja2==3.0.1',
|
|
||||||
'itsdangerous==2.1.2',
|
|
||||||
'click==8.0.1',
|
|
||||||
'MarkupSafe==2.1.3',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'2.1': {
|
|
||||||
'python': '3.10',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': [
|
|
||||||
'click==8.1.3',
|
|
||||||
'itsdangerous==2.1.2',
|
|
||||||
'Jinja2==3.1.2',
|
|
||||||
'MarkupSafe==2.1.1',
|
|
||||||
'Werkzeug==2.3.7',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_FLASK.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.11',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': [
|
|
||||||
'click==8.1.3',
|
|
||||||
'itsdangerous==2.1.2',
|
|
||||||
'Jinja2==3.1.2',
|
|
||||||
'MarkupSafe==2.1.1',
|
|
||||||
'Werkzeug==2.3.7',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['2.2', '2.3']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_DJANGO = {
|
|
||||||
k: {
|
|
||||||
'python': '3.5',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'pre_install': [
|
|
||||||
'apt-get update && apt-get install -y locales',
|
|
||||||
"echo 'en_US UTF-8' > /etc/locale.gen",
|
|
||||||
'locale-gen en_US.UTF-8',
|
|
||||||
],
|
|
||||||
'install': 'python setup.py install',
|
|
||||||
'pip_packages': ['setuptools'],
|
|
||||||
'eval_commands': [
|
|
||||||
'export LANG=en_US.UTF-8',
|
|
||||||
'export LC_ALL=en_US.UTF-8',
|
|
||||||
'export PYTHONIOENCODING=utf8',
|
|
||||||
'export LANGUAGE=en_US:en',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['1.7', '1.8', '1.9', '1.10', '1.11', '2.0', '2.1', '2.2']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_DJANGO.update(
|
|
||||||
{
|
|
||||||
k: {'python': '3.5', 'install': 'python setup.py install'}
|
|
||||||
for k in ['1.4', '1.5', '1.6']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_DJANGO.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.6',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'eval_commands': [
|
|
||||||
"sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen",
|
|
||||||
'export LANG=en_US.UTF-8',
|
|
||||||
'export LANGUAGE=en_US:en',
|
|
||||||
'export LC_ALL=en_US.UTF-8',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['3.0', '3.1', '3.2']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_DJANGO.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.8',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in ['4.0']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_DJANGO.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in ['4.1', '4.2']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_DJANGO.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.11',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in ['5.0']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_REQUESTS = {
|
|
||||||
k: {'python': '3.9', 'packages': 'pytest', 'install': 'python -m pip install .'}
|
|
||||||
for k in ['0.7', '0.8', '0.9', '0.11', '0.13', '0.14', '1.1', '1.2', '2.0', '2.2']
|
|
||||||
+ ['2.3', '2.4', '2.5', '2.7', '2.8', '2.9', '2.10', '2.11', '2.12', '2.17']
|
|
||||||
+ ['2.18', '2.19', '2.22', '2.26', '2.25', '2.27', '3.0']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_SEABORN = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': [
|
|
||||||
'contourpy==1.1.0',
|
|
||||||
'cycler==0.11.0',
|
|
||||||
'fonttools==4.42.1',
|
|
||||||
'importlib-resources==6.0.1',
|
|
||||||
'kiwisolver==1.4.5',
|
|
||||||
'matplotlib==3.7.2',
|
|
||||||
'numpy==1.25.2',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pandas==1.3.5', # 2.0.3
|
|
||||||
'pillow==10.0.0',
|
|
||||||
'pyparsing==3.0.9',
|
|
||||||
'pytest',
|
|
||||||
'python-dateutil==2.8.2',
|
|
||||||
'pytz==2023.3.post1',
|
|
||||||
'scipy==1.11.2',
|
|
||||||
'six==1.16.0',
|
|
||||||
'tzdata==2023.1',
|
|
||||||
'zipp==3.16.2',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['0.11']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_SEABORN.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': 'python -m pip install -e .[dev]',
|
|
||||||
'pip_packages': [
|
|
||||||
'contourpy==1.1.0',
|
|
||||||
'cycler==0.11.0',
|
|
||||||
'fonttools==4.42.1',
|
|
||||||
'importlib-resources==6.0.1',
|
|
||||||
'kiwisolver==1.4.5',
|
|
||||||
'matplotlib==3.7.2',
|
|
||||||
'numpy==1.25.2',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pandas==2.0.0',
|
|
||||||
'pillow==10.0.0',
|
|
||||||
'pyparsing==3.0.9',
|
|
||||||
'pytest',
|
|
||||||
'python-dateutil==2.8.2',
|
|
||||||
'pytz==2023.3.post1',
|
|
||||||
'scipy==1.11.2',
|
|
||||||
'six==1.16.0',
|
|
||||||
'tzdata==2023.1',
|
|
||||||
'zipp==3.16.2',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['0.12', '0.13']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST = {
|
|
||||||
k: {'python': '3.9', 'install': 'python -m pip install -e .'}
|
|
||||||
for k in [
|
|
||||||
'4.4',
|
|
||||||
'4.5',
|
|
||||||
'4.6',
|
|
||||||
'5.0',
|
|
||||||
'5.1',
|
|
||||||
'5.2',
|
|
||||||
'5.3',
|
|
||||||
'5.4',
|
|
||||||
'6.0',
|
|
||||||
'6.2',
|
|
||||||
'6.3',
|
|
||||||
'7.0',
|
|
||||||
'7.1',
|
|
||||||
'7.2',
|
|
||||||
'7.4',
|
|
||||||
'8.0',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['4.4']['pip_packages'] = [
|
|
||||||
'atomicwrites==1.4.1',
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'setuptools==68.0.0',
|
|
||||||
'six==1.16.0',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['4.5']['pip_packages'] = [
|
|
||||||
'atomicwrites==1.4.1',
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'pluggy==0.11.0',
|
|
||||||
'py==1.11.0',
|
|
||||||
'setuptools==68.0.0',
|
|
||||||
'six==1.16.0',
|
|
||||||
'wcwidth==0.2.6',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['4.6']['pip_packages'] = [
|
|
||||||
'atomicwrites==1.4.1',
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'six==1.16.0',
|
|
||||||
'wcwidth==0.2.6',
|
|
||||||
]
|
|
||||||
for k in ['5.0', '5.1', '5.2']:
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST[k]['pip_packages'] = [
|
|
||||||
'atomicwrites==1.4.1',
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'wcwidth==0.2.6',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['5.3']['pip_packages'] = [
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'wcwidth==0.2.6',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['5.4']['pip_packages'] = [
|
|
||||||
'py==1.11.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['6.0']['pip_packages'] = [
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'more-itertools==10.1.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'toml==0.10.2',
|
|
||||||
]
|
|
||||||
for k in ['6.2', '6.3']:
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST[k]['pip_packages'] = [
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'toml==0.10.2',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['7.0']['pip_packages'] = [
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
]
|
|
||||||
for k in ['7.1', '7.2']:
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST[k]['pip_packages'] = [
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==0.13.1',
|
|
||||||
'py==1.11.0',
|
|
||||||
'tomli==2.0.1',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['7.4']['pip_packages'] = [
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==1.3.0',
|
|
||||||
'exceptiongroup==1.1.3',
|
|
||||||
'tomli==2.0.1',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYTEST['8.0']['pip_packages'] = [
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==1.3.0',
|
|
||||||
'exceptiongroup==1.1.3',
|
|
||||||
'tomli==2.0.1',
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_MATPLOTLIB = {
|
|
||||||
k: {
|
|
||||||
'python': '3.11',
|
|
||||||
'packages': 'environment.yml',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pre_install': [
|
|
||||||
'apt-get -y update && apt-get -y upgrade && apt-get install -y imagemagick ffmpeg texlive texlive-latex-extra texlive-fonts-recommended texlive-xetex texlive-luatex cm-super dvipng'
|
|
||||||
],
|
|
||||||
'pip_packages': [
|
|
||||||
'contourpy==1.1.0',
|
|
||||||
'cycler==0.11.0',
|
|
||||||
'fonttools==4.42.1',
|
|
||||||
'ghostscript',
|
|
||||||
'kiwisolver==1.4.5',
|
|
||||||
'numpy==1.25.2',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pillow==10.0.0',
|
|
||||||
'pikepdf',
|
|
||||||
'pyparsing==3.0.9',
|
|
||||||
'python-dateutil==2.8.2',
|
|
||||||
'six==1.16.0',
|
|
||||||
'setuptools==68.1.2',
|
|
||||||
'setuptools-scm==7.1.0',
|
|
||||||
'typing-extensions==4.7.1',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['3.5', '3.6', '3.7']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_MATPLOTLIB.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.8',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pre_install': [
|
|
||||||
'apt-get -y update && apt-get -y upgrade && apt-get install -y imagemagick ffmpeg libfreetype6-dev pkg-config texlive texlive-latex-extra texlive-fonts-recommended texlive-xetex texlive-luatex cm-super'
|
|
||||||
],
|
|
||||||
'pip_packages': ['pytest', 'ipython'],
|
|
||||||
}
|
|
||||||
for k in ['3.1', '3.2', '3.3', '3.4']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_MATPLOTLIB.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.7',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pre_install': [
|
|
||||||
'apt-get -y update && apt-get -y upgrade && apt-get install -y imagemagick ffmpeg libfreetype6-dev pkg-config'
|
|
||||||
],
|
|
||||||
'pip_packages': ['pytest'],
|
|
||||||
}
|
|
||||||
for k in ['3.0']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_MATPLOTLIB.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.5',
|
|
||||||
'install': 'python setup.py build; python setup.py install',
|
|
||||||
'pre_install': [
|
|
||||||
'apt-get -y update && apt-get -y upgrade && && apt-get install -y imagemagick ffmpeg'
|
|
||||||
],
|
|
||||||
'pip_packages': ['pytest'],
|
|
||||||
'execute_test_as_nonroot': True,
|
|
||||||
}
|
|
||||||
for k in ['2.0', '2.1', '2.2', '1.0', '1.1', '1.2', '1.3', '1.4', '1.5']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_SPHINX = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'pip_packages': ['tox==4.16.0', 'tox-current-env==0.0.11'],
|
|
||||||
'install': 'python -m pip install -e .[test]',
|
|
||||||
'pre_install': ["sed -i 's/pytest/pytest -rA/' tox.ini"],
|
|
||||||
}
|
|
||||||
for k in ['1.5', '1.6', '1.7', '1.8', '2.0', '2.1', '2.2', '2.3', '2.4', '3.0']
|
|
||||||
+ ['3.1', '3.2', '3.3', '3.4', '3.5', '4.0', '4.1', '4.2', '4.3', '4.4']
|
|
||||||
+ ['4.5', '5.0', '5.1', '5.2', '5.3', '6.0', '6.2', '7.0', '7.1', '7.2']
|
|
||||||
}
|
|
||||||
for k in ['3.0', '3.1', '3.2', '3.3', '3.4', '3.5', '4.0', '4.1', '4.2', '4.3', '4.4']:
|
|
||||||
MAP_VERSION_TO_INSTALL_SPHINX[k]['pre_install'].extend(
|
|
||||||
[
|
|
||||||
"sed -i 's/Jinja2>=2.3/Jinja2<3.0/' setup.py",
|
|
||||||
"sed -i 's/sphinxcontrib-applehelp/sphinxcontrib-applehelp<=1.0.7/' setup.py",
|
|
||||||
"sed -i 's/sphinxcontrib-devhelp/sphinxcontrib-devhelp<=1.0.5/' setup.py",
|
|
||||||
"sed -i 's/sphinxcontrib-qthelp/sphinxcontrib-qthelp<=1.0.6/' setup.py",
|
|
||||||
"sed -i 's/alabaster>=0.7,<0.8/alabaster>=0.7,<0.7.12/' setup.py",
|
|
||||||
"sed -i \"s/'packaging',/'packaging', 'markupsafe<=2.0.1',/\" setup.py",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if k in ['4.2', '4.3', '4.4']:
|
|
||||||
MAP_VERSION_TO_INSTALL_SPHINX[k]['pre_install'].extend(
|
|
||||||
[
|
|
||||||
"sed -i 's/sphinxcontrib-htmlhelp>=2.0.0/sphinxcontrib-htmlhelp>=2.0.0,<=2.0.4/' setup.py",
|
|
||||||
"sed -i 's/sphinxcontrib-serializinghtml>=1.1.5/sphinxcontrib-serializinghtml>=1.1.5,<=1.1.9/' setup.py",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
elif k == '4.1':
|
|
||||||
MAP_VERSION_TO_INSTALL_SPHINX[k]['pre_install'].extend(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"grep -q 'sphinxcontrib-htmlhelp>=2.0.0' setup.py && "
|
|
||||||
"sed -i 's/sphinxcontrib-htmlhelp>=2.0.0/sphinxcontrib-htmlhelp>=2.0.0,<=2.0.4/' setup.py || "
|
|
||||||
"sed -i 's/sphinxcontrib-htmlhelp/sphinxcontrib-htmlhelp<=2.0.4/' setup.py"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"grep -q 'sphinxcontrib-serializinghtml>=1.1.5' setup.py && "
|
|
||||||
"sed -i 's/sphinxcontrib-serializinghtml>=1.1.5/sphinxcontrib-serializinghtml>=1.1.5,<=1.1.9/' setup.py || "
|
|
||||||
"sed -i 's/sphinxcontrib-serializinghtml/sphinxcontrib-serializinghtml<=1.1.9/' setup.py"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
MAP_VERSION_TO_INSTALL_SPHINX[k]['pre_install'].extend(
|
|
||||||
[
|
|
||||||
"sed -i 's/sphinxcontrib-htmlhelp/sphinxcontrib-htmlhelp<=2.0.4/' setup.py",
|
|
||||||
"sed -i 's/sphinxcontrib-serializinghtml/sphinxcontrib-serializinghtml<=1.1.9/' setup.py",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_SPHINX['7.2']['pre_install'] += [
|
|
||||||
'apt-get update && apt-get install -y graphviz'
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_ASTROPY = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': 'python -m pip install -e .[test] --verbose',
|
|
||||||
'pip_packages': [
|
|
||||||
'attrs==23.1.0',
|
|
||||||
'exceptiongroup==1.1.3',
|
|
||||||
'execnet==2.0.2',
|
|
||||||
'hypothesis==6.82.6',
|
|
||||||
'iniconfig==2.0.0',
|
|
||||||
'numpy==1.25.2',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pluggy==1.3.0',
|
|
||||||
'psutil==5.9.5',
|
|
||||||
'pyerfa==2.0.0.3',
|
|
||||||
'pytest-arraydiff==0.5.0',
|
|
||||||
'pytest-astropy-header==0.2.2',
|
|
||||||
'pytest-astropy==0.10.0',
|
|
||||||
'pytest-cov==4.1.0',
|
|
||||||
'pytest-doctestplus==1.0.0',
|
|
||||||
'pytest-filter-subpackage==0.1.2',
|
|
||||||
'pytest-mock==3.11.1',
|
|
||||||
'pytest-openfiles==0.5.0',
|
|
||||||
'pytest-remotedata==0.4.0',
|
|
||||||
'pytest-xdist==3.3.1',
|
|
||||||
'pytest==7.4.0',
|
|
||||||
'PyYAML==6.0.1',
|
|
||||||
'setuptools==68.0.0',
|
|
||||||
'sortedcontainers==2.4.0',
|
|
||||||
'tomli==2.0.1',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for k in ['0.1', '0.2', '0.3', '0.4', '1.1', '1.2', '1.3', '3.0', '3.1', '3.2']
|
|
||||||
+ ['4.1', '4.2', '4.3', '5.0', '5.1', '5.2']
|
|
||||||
}
|
|
||||||
for k in ['4.1', '4.2', '4.3', '5.0', '5.1', '5.2']:
|
|
||||||
MAP_VERSION_TO_INSTALL_ASTROPY[k]['pre_install'] = [
|
|
||||||
'sed -i \'s/requires = \\["setuptools",/requires = \\["setuptools==68.0.0",/\' pyproject.toml'
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_SYMPY = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'mpmath flake8',
|
|
||||||
'pip_packages': ['mpmath==1.3.0', 'flake8-comprehensions'],
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in ['0.7', '1.0', '1.1', '1.10', '1.11', '1.12', '1.2', '1.4', '1.5', '1.6']
|
|
||||||
+ ['1.7', '1.8', '1.9']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_SYMPY.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': ['mpmath==1.3.0'],
|
|
||||||
}
|
|
||||||
for k in ['1.13']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_PYLINT = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in [
|
|
||||||
'2.10',
|
|
||||||
'2.11',
|
|
||||||
'2.13',
|
|
||||||
'2.14',
|
|
||||||
'2.15',
|
|
||||||
'2.16',
|
|
||||||
'2.17',
|
|
||||||
'2.8',
|
|
||||||
'2.9',
|
|
||||||
'3.0',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PYLINT['2.8']['pip_packages'] = ['pyenchant==3.2']
|
|
||||||
MAP_VERSION_TO_INSTALL_PYLINT['2.8']['pre_install'] = [
|
|
||||||
'apt-get update && apt-get install -y libenchant-2-dev hunspell-en-us'
|
|
||||||
]
|
|
||||||
MAP_VERSION_TO_INSTALL_PYLINT.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
**MAP_VERSION_TO_INSTALL_PYLINT[k],
|
|
||||||
'pip_packages': ['astroid==3.0.0a6', 'setuptools'],
|
|
||||||
}
|
|
||||||
for k in ['3.0']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
MAP_VERSION_TO_INSTALL_XARRAY = {
|
|
||||||
k: {
|
|
||||||
'python': '3.10',
|
|
||||||
'packages': 'environment.yml',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': [
|
|
||||||
'numpy==1.23.0',
|
|
||||||
'packaging==23.1',
|
|
||||||
'pandas==1.5.3',
|
|
||||||
'pytest==7.4.0',
|
|
||||||
'python-dateutil==2.8.2',
|
|
||||||
'pytz==2023.3',
|
|
||||||
'six==1.16.0',
|
|
||||||
'scipy==1.11.1',
|
|
||||||
'setuptools==68.0.0',
|
|
||||||
],
|
|
||||||
'no_use_env': True,
|
|
||||||
}
|
|
||||||
for k in ['0.12', '0.18', '0.19', '0.20', '2022.03', '2022.06', '2022.09']
|
|
||||||
}
|
|
||||||
|
|
||||||
MAP_VERSION_TO_INSTALL_SQLFLUFF = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in [
|
|
||||||
'0.10',
|
|
||||||
'0.11',
|
|
||||||
'0.12',
|
|
||||||
'0.13',
|
|
||||||
'0.4',
|
|
||||||
'0.5',
|
|
||||||
'0.6',
|
|
||||||
'0.8',
|
|
||||||
'0.9',
|
|
||||||
'1.0',
|
|
||||||
'1.1',
|
|
||||||
'1.2',
|
|
||||||
'1.3',
|
|
||||||
'1.4',
|
|
||||||
'2.0',
|
|
||||||
'2.1',
|
|
||||||
'2.2',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_DBT_CORE = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
}
|
|
||||||
for k in [
|
|
||||||
'0.13',
|
|
||||||
'0.14',
|
|
||||||
'0.15',
|
|
||||||
'0.16',
|
|
||||||
'0.17',
|
|
||||||
'0.18',
|
|
||||||
'0.19',
|
|
||||||
'0.20',
|
|
||||||
'0.21',
|
|
||||||
'1.0',
|
|
||||||
'1.1',
|
|
||||||
'1.2',
|
|
||||||
'1.3',
|
|
||||||
'1.4',
|
|
||||||
'1.5',
|
|
||||||
'1.6',
|
|
||||||
'1.7',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PYVISTA = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': ['pytest'],
|
|
||||||
}
|
|
||||||
for k in ['0.20', '0.21', '0.22', '0.23']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PYVISTA.update(
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'packages': 'requirements.txt',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': ['pytest'],
|
|
||||||
}
|
|
||||||
for k in [
|
|
||||||
'0.24',
|
|
||||||
'0.25',
|
|
||||||
'0.26',
|
|
||||||
'0.27',
|
|
||||||
'0.28',
|
|
||||||
'0.29',
|
|
||||||
'0.30',
|
|
||||||
'0.31',
|
|
||||||
'0.32',
|
|
||||||
'0.33',
|
|
||||||
'0.34',
|
|
||||||
'0.35',
|
|
||||||
'0.36',
|
|
||||||
'0.37',
|
|
||||||
'0.38',
|
|
||||||
'0.39',
|
|
||||||
'0.40',
|
|
||||||
'0.41',
|
|
||||||
'0.42',
|
|
||||||
'0.43',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_ASTROID = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': 'python -m pip install -e .',
|
|
||||||
'pip_packages': ['pytest'],
|
|
||||||
}
|
|
||||||
for k in [
|
|
||||||
'2.10',
|
|
||||||
'2.12',
|
|
||||||
'2.13',
|
|
||||||
'2.14',
|
|
||||||
'2.15',
|
|
||||||
'2.16',
|
|
||||||
'2.5',
|
|
||||||
'2.6',
|
|
||||||
'2.7',
|
|
||||||
'2.8',
|
|
||||||
'2.9',
|
|
||||||
'3.0',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_MARSHMALLOW = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': "python -m pip install -e '.[dev]'",
|
|
||||||
}
|
|
||||||
for k in [
|
|
||||||
'2.18',
|
|
||||||
'2.19',
|
|
||||||
'2.20',
|
|
||||||
'3.0',
|
|
||||||
'3.1',
|
|
||||||
'3.10',
|
|
||||||
'3.11',
|
|
||||||
'3.12',
|
|
||||||
'3.13',
|
|
||||||
'3.15',
|
|
||||||
'3.16',
|
|
||||||
'3.19',
|
|
||||||
'3.2',
|
|
||||||
'3.4',
|
|
||||||
'3.8',
|
|
||||||
'3.9',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PVLIB = {
|
|
||||||
k: {
|
|
||||||
'python': '3.9',
|
|
||||||
'install': 'python -m pip install -e .[all]',
|
|
||||||
'packages': 'pandas scipy',
|
|
||||||
'pip_packages': ['jupyter', 'ipython', 'matplotlib', 'pytest', 'flake8'],
|
|
||||||
}
|
|
||||||
for k in ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9']
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PYDICOM = {
|
|
||||||
k: {'python': '3.6', 'install': 'python -m pip install -e .', 'packages': 'numpy'}
|
|
||||||
for k in [
|
|
||||||
'1.0',
|
|
||||||
'1.1',
|
|
||||||
'1.2',
|
|
||||||
'1.3',
|
|
||||||
'1.4',
|
|
||||||
'2.0',
|
|
||||||
'2.1',
|
|
||||||
'2.2',
|
|
||||||
'2.3',
|
|
||||||
'2.4',
|
|
||||||
'3.0',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
MAP_VERSION_TO_INSTALL_PYDICOM.update(
|
|
||||||
{k: {**MAP_VERSION_TO_INSTALL_PYDICOM[k], 'python': '3.8'} for k in ['1.4', '2.0']}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_PYDICOM.update(
|
|
||||||
{k: {**MAP_VERSION_TO_INSTALL_PYDICOM[k], 'python': '3.9'} for k in ['2.1', '2.2']}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_PYDICOM.update(
|
|
||||||
{k: {**MAP_VERSION_TO_INSTALL_PYDICOM[k], 'python': '3.10'} for k in ['2.3']}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_PYDICOM.update(
|
|
||||||
{k: {**MAP_VERSION_TO_INSTALL_PYDICOM[k], 'python': '3.11'} for k in ['2.4', '3.0']}
|
|
||||||
)
|
|
||||||
MAP_VERSION_TO_INSTALL_HUMANEVAL = {k: {'python': '3.9'} for k in ['1.0']}
|
|
||||||
MAP_VERSION_TO_INSTALL_HUMANEVAL_FIX = {
|
|
||||||
k: {'python': '3.10', 'packages': 'pytest'} for k in ['0.0.1']
|
|
||||||
}
|
|
||||||
|
|
||||||
# Constants - Task Instance Instllation Environment
|
|
||||||
MAP_VERSION_TO_INSTALL = {
|
|
||||||
'astropy/astropy': MAP_VERSION_TO_INSTALL_ASTROPY,
|
|
||||||
'dbt-labs/dbt-core': MAP_VERSION_TO_INSTALL_DBT_CORE,
|
|
||||||
'django/django': MAP_VERSION_TO_INSTALL_DJANGO,
|
|
||||||
'matplotlib/matplotlib': MAP_VERSION_TO_INSTALL_MATPLOTLIB,
|
|
||||||
'marshmallow-code/marshmallow': MAP_VERSION_TO_INSTALL_MARSHMALLOW,
|
|
||||||
'mwaskom/seaborn': MAP_VERSION_TO_INSTALL_SEABORN,
|
|
||||||
'pallets/flask': MAP_VERSION_TO_INSTALL_FLASK,
|
|
||||||
'psf/requests': MAP_VERSION_TO_INSTALL_REQUESTS,
|
|
||||||
'pvlib/pvlib-python': MAP_VERSION_TO_INSTALL_PVLIB,
|
|
||||||
'pydata/xarray': MAP_VERSION_TO_INSTALL_XARRAY,
|
|
||||||
'pydicom/pydicom': MAP_VERSION_TO_INSTALL_PYDICOM,
|
|
||||||
'pylint-dev/astroid': MAP_VERSION_TO_INSTALL_ASTROID,
|
|
||||||
'pylint-dev/pylint': MAP_VERSION_TO_INSTALL_PYLINT,
|
|
||||||
'pytest-dev/pytest': MAP_VERSION_TO_INSTALL_PYTEST,
|
|
||||||
'pyvista/pyvista': MAP_VERSION_TO_INSTALL_PYVISTA,
|
|
||||||
'scikit-learn/scikit-learn': MAP_VERSION_TO_INSTALL_SKLEARN,
|
|
||||||
'sphinx-doc/sphinx': MAP_VERSION_TO_INSTALL_SPHINX,
|
|
||||||
'sqlfluff/sqlfluff': MAP_VERSION_TO_INSTALL_SQLFLUFF,
|
|
||||||
'swe-bench/humaneval': MAP_VERSION_TO_INSTALL_HUMANEVAL,
|
|
||||||
'nielstron/humaneval_fix': MAP_VERSION_TO_INSTALL_HUMANEVAL_FIX,
|
|
||||||
'sympy/sympy': MAP_VERSION_TO_INSTALL_SYMPY,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Constants - Repository Specific Installation Instructions
|
|
||||||
MAP_REPO_TO_INSTALL = {}
|
|
||||||
|
|
||||||
# Constants - Task Instance Test Frameworks
|
|
||||||
TEST_PYTEST_VERBOSE = 'pytest -rA --tb=long -p no:cacheprovider'
|
|
||||||
MAP_REPO_TO_TEST_FRAMEWORK_VERBOSE = {
|
|
||||||
'astropy/astropy': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_ASTROPY.keys()
|
|
||||||
},
|
|
||||||
'django/django': {
|
|
||||||
k: './tests/runtests.py --verbosity 2 --settings=test_sqlite --parallel 1'
|
|
||||||
for k in MAP_VERSION_TO_INSTALL_DJANGO.keys()
|
|
||||||
},
|
|
||||||
'marshmallow-code/marshmallow': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_MARSHMALLOW.keys()
|
|
||||||
},
|
|
||||||
'matplotlib/matplotlib': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_MATPLOTLIB.keys()
|
|
||||||
},
|
|
||||||
'mwaskom/seaborn': {
|
|
||||||
k: 'pytest -rA --tb=long' for k in MAP_VERSION_TO_INSTALL_SEABORN.keys()
|
|
||||||
},
|
|
||||||
'pallets/flask': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_FLASK.keys()
|
|
||||||
},
|
|
||||||
'psf/requests': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_REQUESTS.keys()
|
|
||||||
},
|
|
||||||
'pvlib/pvlib-python': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_PVLIB.keys()
|
|
||||||
},
|
|
||||||
'pydata/xarray': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_XARRAY.keys()
|
|
||||||
},
|
|
||||||
'pydicom/pydicom': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_PYDICOM.keys()
|
|
||||||
},
|
|
||||||
'pylint-dev/astroid': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_ASTROID.keys()
|
|
||||||
},
|
|
||||||
'pylint-dev/pylint': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_PYLINT.keys()
|
|
||||||
},
|
|
||||||
'pytest-dev/pytest': {
|
|
||||||
k: 'pytest -rA --tb=long' for k in MAP_VERSION_TO_INSTALL_PYTEST.keys()
|
|
||||||
},
|
|
||||||
'pyvista/pyvista': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_PYVISTA.keys()
|
|
||||||
},
|
|
||||||
'scikit-learn/scikit-learn': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_SKLEARN.keys()
|
|
||||||
},
|
|
||||||
'sphinx-doc/sphinx': {
|
|
||||||
k: 'tox -epy39 -v --' for k in MAP_VERSION_TO_INSTALL_SPHINX.keys()
|
|
||||||
},
|
|
||||||
'sqlfluff/sqlfluff': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_SQLFLUFF.keys()
|
|
||||||
},
|
|
||||||
'swe-bench/humaneval': {
|
|
||||||
k: 'python' for k in MAP_VERSION_TO_INSTALL_HUMANEVAL.keys()
|
|
||||||
},
|
|
||||||
'nielstron/humaneval_fix': {
|
|
||||||
k: TEST_PYTEST_VERBOSE for k in MAP_VERSION_TO_INSTALL_HUMANEVAL.keys()
|
|
||||||
},
|
|
||||||
'sympy/sympy': {
|
|
||||||
k: 'bin/test -C --verbose' for k in MAP_VERSION_TO_INSTALL_SYMPY.keys()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
MAP_REPO_TO_TEST_FRAMEWORK_VERBOSE['django/django']['1.9'] = (
|
|
||||||
'./tests/runtests.py --verbosity 2'
|
|
||||||
)
|
|
||||||
@@ -3,7 +3,7 @@ import copy
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Any, Literal
|
from typing import Any
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import toml
|
import toml
|
||||||
@@ -17,11 +17,6 @@ from evaluation.benchmarks.swe_bench.binary_patch_utils import (
|
|||||||
from evaluation.benchmarks.swe_bench.resource.mapping import (
|
from evaluation.benchmarks.swe_bench.resource.mapping import (
|
||||||
get_instance_resource_factor,
|
get_instance_resource_factor,
|
||||||
)
|
)
|
||||||
from evaluation.benchmarks.swe_bench.resource.swt_bench_constants import (
|
|
||||||
MAP_REPO_TO_INSTALL,
|
|
||||||
MAP_REPO_TO_TEST_FRAMEWORK_VERBOSE,
|
|
||||||
MAP_VERSION_TO_INSTALL,
|
|
||||||
)
|
|
||||||
from evaluation.utils.shared import (
|
from evaluation.utils.shared import (
|
||||||
EvalException,
|
EvalException,
|
||||||
EvalMetadata,
|
EvalMetadata,
|
||||||
@@ -35,6 +30,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
update_llm_config_for_completions_logging,
|
update_llm_config_for_completions_logging,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
@@ -60,7 +56,6 @@ from openhands.utils.shutdown_listener import sleep_if_should_continue
|
|||||||
|
|
||||||
USE_HINT_TEXT = os.environ.get('USE_HINT_TEXT', 'false').lower() == 'true'
|
USE_HINT_TEXT = os.environ.get('USE_HINT_TEXT', 'false').lower() == 'true'
|
||||||
RUN_WITH_BROWSING = os.environ.get('RUN_WITH_BROWSING', 'false').lower() == 'true'
|
RUN_WITH_BROWSING = os.environ.get('RUN_WITH_BROWSING', 'false').lower() == 'true'
|
||||||
BenchMode = Literal['swe', 'swt', 'swt-ci']
|
|
||||||
|
|
||||||
|
|
||||||
AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
|
AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
|
||||||
@@ -74,36 +69,7 @@ def _get_swebench_workspace_dir_name(instance: pd.Series) -> str:
|
|||||||
|
|
||||||
def get_instruction(instance: pd.Series, metadata: EvalMetadata) -> MessageAction:
|
def get_instruction(instance: pd.Series, metadata: EvalMetadata) -> MessageAction:
|
||||||
workspace_dir_name = _get_swebench_workspace_dir_name(instance)
|
workspace_dir_name = _get_swebench_workspace_dir_name(instance)
|
||||||
mode = metadata.details['mode']
|
instruction = f"""
|
||||||
if mode.startswith('swt'):
|
|
||||||
test_instructions = (
|
|
||||||
f'The following command can be used to run the tests: `{list(MAP_REPO_TO_TEST_FRAMEWORK_VERBOSE[instance.repo].values())[0]}`. Make sure they fail in the expected way.\n'
|
|
||||||
if mode.endswith('ci')
|
|
||||||
else ''
|
|
||||||
)
|
|
||||||
instruction = f"""\
|
|
||||||
<uploaded_files>
|
|
||||||
/workspace/{workspace_dir_name}
|
|
||||||
</uploaded_files>
|
|
||||||
I've uploaded a python code repository in the directory {workspace_dir_name}. Consider the following issue description:
|
|
||||||
|
|
||||||
<issue_description>
|
|
||||||
{instance.problem_statement}
|
|
||||||
</issue_description>
|
|
||||||
|
|
||||||
|
|
||||||
Can you help me implement the necessary changes to the repository to test whether the issue in <issue_description> was resolved?
|
|
||||||
I will take care of all changes to any of the non-test files. This means you DON'T have to modify the actual logic and ONLY have to update test logic and tests!
|
|
||||||
Your task is to make the minimal changes to tests files in the /workspace directory to reproduce the issue in the <issue_description>, i.e., such that the generated tests fail in the current state (where the issue is unresolved) and pass when the issue will be resolved.
|
|
||||||
Follow these steps to reproduce the issue:
|
|
||||||
1. As a first step, it might be a good idea to explore the repo to familiarize yourself with its structure.
|
|
||||||
2. Create a script `reproduction.py` to reproduce the error and execute it with `python reproduction.py` using the BashTool, to confirm the error
|
|
||||||
3. Edit the sourcecode of the repo to integrate your reproduction script into the test framework
|
|
||||||
4. Run the test framework and make sure your tests fail! Only submit FAILING tests! Never submit passing tests.
|
|
||||||
{test_instructions}Your thinking should be thorough and so it's fine if it's very long.
|
|
||||||
"""
|
|
||||||
else:
|
|
||||||
instruction = f"""
|
|
||||||
<uploaded_files>
|
<uploaded_files>
|
||||||
/workspace/{workspace_dir_name}
|
/workspace/{workspace_dir_name}
|
||||||
</uploaded_files>
|
</uploaded_files>
|
||||||
@@ -266,6 +232,7 @@ def get_config(
|
|||||||
condenser=metadata.condenser_config,
|
condenser=metadata.condenser_config,
|
||||||
enable_prompt_extensions=False,
|
enable_prompt_extensions=False,
|
||||||
)
|
)
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
config.set_agent_config(agent_config)
|
config.set_agent_config(agent_config)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@@ -391,30 +358,6 @@ def initialize_runtime(
|
|||||||
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
|
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
|
||||||
assert_and_raise(obs.exit_code == 0, f'Failed to remove git remotes: {str(obs)}')
|
assert_and_raise(obs.exit_code == 0, f'Failed to remove git remotes: {str(obs)}')
|
||||||
|
|
||||||
if metadata.details['mode'] == 'swt-ci':
|
|
||||||
# set up repo
|
|
||||||
setup_commands = []
|
|
||||||
if instance['repo'] in MAP_REPO_TO_INSTALL:
|
|
||||||
setup_commands.append(MAP_REPO_TO_INSTALL[instance['repo']])
|
|
||||||
|
|
||||||
# Run pre-install set up if provided
|
|
||||||
install = MAP_VERSION_TO_INSTALL.get(instance['repo'], {}).get(
|
|
||||||
instance['version'], []
|
|
||||||
)
|
|
||||||
if 'pre_install' in install:
|
|
||||||
for pre_install in install['pre_install']:
|
|
||||||
setup_commands.append(pre_install)
|
|
||||||
|
|
||||||
if 'install' in install:
|
|
||||||
setup_commands.append(install['install'])
|
|
||||||
|
|
||||||
for command in setup_commands:
|
|
||||||
action = CmdRunAction(command=command)
|
|
||||||
action.set_hard_timeout(600)
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
|
|
||||||
|
|
||||||
if 'multimodal' not in metadata.dataset.lower():
|
if 'multimodal' not in metadata.dataset.lower():
|
||||||
# Only for non-multimodal datasets, we need to activate the testbed environment for Python
|
# Only for non-multimodal datasets, we need to activate the testbed environment for Python
|
||||||
# SWE-Bench multimodal datasets are not using the testbed environment
|
# SWE-Bench multimodal datasets are not using the testbed environment
|
||||||
@@ -737,13 +680,6 @@ if __name__ == '__main__':
|
|||||||
default='test',
|
default='test',
|
||||||
help='split to evaluate on',
|
help='split to evaluate on',
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
'--mode',
|
|
||||||
type=str,
|
|
||||||
default='swe',
|
|
||||||
choices=['swe', 'swt', 'swt-ci'],
|
|
||||||
help="mode to run the evaluation, either 'swe', 'swt', or 'swt-ci'",
|
|
||||||
)
|
|
||||||
args, _ = parser.parse_known_args()
|
args, _ = parser.parse_known_args()
|
||||||
|
|
||||||
# NOTE: It is preferable to load datasets from huggingface datasets and perform post-processing
|
# NOTE: It is preferable to load datasets from huggingface datasets and perform post-processing
|
||||||
@@ -780,7 +716,7 @@ if __name__ == '__main__':
|
|||||||
if llm_config is None:
|
if llm_config is None:
|
||||||
raise ValueError(f'Could not find LLM config: --llm_config {args.llm_config}')
|
raise ValueError(f'Could not find LLM config: --llm_config {args.llm_config}')
|
||||||
|
|
||||||
details = {'mode': args.mode}
|
details = {}
|
||||||
_agent_cls = openhands.agenthub.Agent.get_cls(args.agent_cls)
|
_agent_cls = openhands.agenthub.Agent.get_cls(args.agent_cls)
|
||||||
|
|
||||||
dataset_descrption = (
|
dataset_descrption = (
|
||||||
@@ -930,7 +866,7 @@ if __name__ == '__main__':
|
|||||||
# Also make sure git_patch is not empty - otherwise we fall back to previous attempt (empty patch is worse than anything else)
|
# Also make sure git_patch is not empty - otherwise we fall back to previous attempt (empty patch is worse than anything else)
|
||||||
if (
|
if (
|
||||||
instance['instance_id'] not in added_instance_ids
|
instance['instance_id'] not in added_instance_ids
|
||||||
and instance['test_result'].get('git_patch', '').strip()
|
and instance['test_result']['git_patch'].strip()
|
||||||
):
|
):
|
||||||
fout.write(line)
|
fout.write(line)
|
||||||
added_instance_ids.add(instance['instance_id'])
|
added_instance_ids.add(instance['instance_id'])
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ NUM_WORKERS=$6
|
|||||||
DATASET=$7
|
DATASET=$7
|
||||||
SPLIT=$8
|
SPLIT=$8
|
||||||
N_RUNS=$9
|
N_RUNS=$9
|
||||||
MODE=${10}
|
|
||||||
|
|
||||||
if [ -z "$NUM_WORKERS" ]; then
|
if [ -z "$NUM_WORKERS" ]; then
|
||||||
NUM_WORKERS=1
|
NUM_WORKERS=1
|
||||||
@@ -46,11 +45,6 @@ if [ -z "$SPLIT" ]; then
|
|||||||
SPLIT="test"
|
SPLIT="test"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$MODE" ]; then
|
|
||||||
MODE="swe"
|
|
||||||
echo "MODE not specified, use default $MODE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export RUN_WITH_BROWSING=$RUN_WITH_BROWSING
|
export RUN_WITH_BROWSING=$RUN_WITH_BROWSING
|
||||||
echo "RUN_WITH_BROWSING: $RUN_WITH_BROWSING"
|
echo "RUN_WITH_BROWSING: $RUN_WITH_BROWSING"
|
||||||
|
|
||||||
@@ -61,10 +55,6 @@ echo "OPENHANDS_VERSION: $OPENHANDS_VERSION"
|
|||||||
echo "MODEL_CONFIG: $MODEL_CONFIG"
|
echo "MODEL_CONFIG: $MODEL_CONFIG"
|
||||||
echo "DATASET: $DATASET"
|
echo "DATASET: $DATASET"
|
||||||
echo "SPLIT: $SPLIT"
|
echo "SPLIT: $SPLIT"
|
||||||
echo "MAX_ITER: $MAX_ITER"
|
|
||||||
echo "NUM_WORKERS: $NUM_WORKERS"
|
|
||||||
echo "COMMIT_HASH: $COMMIT_HASH"
|
|
||||||
echo "MODE: $MODE"
|
|
||||||
|
|
||||||
# Default to NOT use Hint
|
# Default to NOT use Hint
|
||||||
if [ -z "$USE_HINT_TEXT" ]; then
|
if [ -z "$USE_HINT_TEXT" ]; then
|
||||||
@@ -84,13 +74,9 @@ fi
|
|||||||
if [ -n "$EXP_NAME" ]; then
|
if [ -n "$EXP_NAME" ]; then
|
||||||
EVAL_NOTE="$EVAL_NOTE-$EXP_NAME"
|
EVAL_NOTE="$EVAL_NOTE-$EXP_NAME"
|
||||||
fi
|
fi
|
||||||
# if mode != swe, add mode to the eval note
|
|
||||||
if [ "$MODE" != "swe" ]; then
|
|
||||||
EVAL_NOTE="${EVAL_NOTE}-${MODE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
function run_eval() {
|
function run_eval() {
|
||||||
local eval_note="${1}"
|
local eval_note=$1
|
||||||
COMMAND="poetry run python evaluation/benchmarks/swe_bench/run_infer.py \
|
COMMAND="poetry run python evaluation/benchmarks/swe_bench/run_infer.py \
|
||||||
--agent-cls $AGENT \
|
--agent-cls $AGENT \
|
||||||
--llm-config $MODEL_CONFIG \
|
--llm-config $MODEL_CONFIG \
|
||||||
@@ -98,8 +84,7 @@ function run_eval() {
|
|||||||
--eval-num-workers $NUM_WORKERS \
|
--eval-num-workers $NUM_WORKERS \
|
||||||
--eval-note $eval_note \
|
--eval-note $eval_note \
|
||||||
--dataset $DATASET \
|
--dataset $DATASET \
|
||||||
--split $SPLIT \
|
--split $SPLIT"
|
||||||
--mode $MODE"
|
|
||||||
|
|
||||||
if [ -n "$EVAL_LIMIT" ]; then
|
if [ -n "$EVAL_LIMIT" ]; then
|
||||||
echo "EVAL_LIMIT: $EVAL_LIMIT"
|
echo "EVAL_LIMIT: $EVAL_LIMIT"
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import unidiff
|
|
||||||
|
|
||||||
from evaluation.benchmarks.swe_bench.resource.swt_bench_constants import (
|
|
||||||
MAP_VERSION_TO_INSTALL,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_setup_files(model_patch: str, instance: dict, delete_setup_changes: bool):
|
|
||||||
"""Discard all changes that a patch applies to files changes by the pre_install script and that are reproduction scripts (top-level script)"""
|
|
||||||
setup_files = ['setup.py', 'tox.ini', 'pyproject.toml']
|
|
||||||
pre_install = (
|
|
||||||
MAP_VERSION_TO_INSTALL.get(instance['repo'], {})
|
|
||||||
.get(instance['version'], {})
|
|
||||||
.get('pre_install', [])
|
|
||||||
)
|
|
||||||
relevant_files = (
|
|
||||||
[
|
|
||||||
file
|
|
||||||
for file in setup_files
|
|
||||||
if any(file in install and 'sed' in install for install in pre_install)
|
|
||||||
]
|
|
||||||
if delete_setup_changes
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
for i in range(10):
|
|
||||||
try:
|
|
||||||
# Appearently outputs.jsonl has .strip() applied, so we try to reconstruct the original patch by adding auxiliary whitespace
|
|
||||||
patch = unidiff.PatchSet(model_patch + i * '\n')
|
|
||||||
break
|
|
||||||
except unidiff.UnidiffParseError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
to_delete = []
|
|
||||||
for i, file in enumerate(patch):
|
|
||||||
if (
|
|
||||||
any(f in file.source_file for f in relevant_files)
|
|
||||||
or file.target_file.count('/') == 1
|
|
||||||
):
|
|
||||||
to_delete.append(i)
|
|
||||||
for i in reversed(to_delete):
|
|
||||||
del patch[i]
|
|
||||||
return str(patch)
|
|
||||||
|
|
||||||
|
|
||||||
def main(
|
|
||||||
prediction_file: str,
|
|
||||||
):
|
|
||||||
"""Main function to extract the model patches from the OpenHands prediction file and turn them into the expected SWT-Bench format."""
|
|
||||||
with open(prediction_file) as f:
|
|
||||||
for line in f:
|
|
||||||
pred = json.loads(line)
|
|
||||||
try:
|
|
||||||
git_diff = pred['test_result']['git_patch']
|
|
||||||
except KeyError:
|
|
||||||
_LOGGER.warning(
|
|
||||||
'Warning: No git diff found for instance %s', pred['instance_id']
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
ci_mode = pred['metadata']['details'].get('mode', '') == 'swt-ci'
|
|
||||||
try:
|
|
||||||
git_diff = remove_setup_files(git_diff, pred['instance'], ci_mode)
|
|
||||||
except: # noqa: E722
|
|
||||||
_LOGGER.warning(
|
|
||||||
'Warning: Invalid git diff found for instance %s',
|
|
||||||
pred['instance_id'],
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
json.dumps(
|
|
||||||
{
|
|
||||||
'instance_id': pred['instance_id'],
|
|
||||||
'model_name_or_path': f'{pred["metadata"]["llm_config"]["openrouter_app_name"]}__{pred["metadata"]["agent_class"]}__{pred["metadata"]["llm_config"]["model"]}',
|
|
||||||
'model_patch': git_diff,
|
|
||||||
'full_output': json.dumps(pred),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument(
|
|
||||||
'--prediction_file',
|
|
||||||
type=str,
|
|
||||||
required=True,
|
|
||||||
help='Path to the prediction file (.../outputs.jsonl)',
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
main(args.prediction_file)
|
|
||||||
@@ -30,6 +30,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
update_llm_config_for_completions_logging,
|
update_llm_config_for_completions_logging,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
@@ -164,6 +165,7 @@ def get_config(
|
|||||||
condenser=metadata.condenser_config,
|
condenser=metadata.condenser_config,
|
||||||
enable_prompt_extensions=False,
|
enable_prompt_extensions=False,
|
||||||
)
|
)
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
config.set_agent_config(agent_config)
|
config.set_agent_config(agent_config)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ from typing import List
|
|||||||
import yaml
|
import yaml
|
||||||
from browsing import pre_login
|
from browsing import pre_login
|
||||||
|
|
||||||
from evaluation.utils.shared import get_default_sandbox_config_for_eval
|
from evaluation.utils.shared import (
|
||||||
|
get_default_sandbox_config_for_eval,
|
||||||
|
update_agent_config_for_eval,
|
||||||
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
AppConfig,
|
AppConfig,
|
||||||
@@ -58,12 +61,14 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(llm_config)
|
config.set_llm_config(llm_config)
|
||||||
if agent_config:
|
if agent_config:
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
config.set_agent_config(agent_config)
|
config.set_agent_config(agent_config)
|
||||||
else:
|
else:
|
||||||
logger.info('Agent config not provided, using default settings')
|
logger.info('Agent config not provided, using default settings')
|
||||||
agent_config = AgentConfig(
|
agent_config = AgentConfig(
|
||||||
enable_prompt_extensions=False,
|
enable_prompt_extensions=False,
|
||||||
)
|
)
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
config.set_agent_config(agent_config)
|
config.set_agent_config(agent_config)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -55,6 +56,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from evaluation.utils.shared import (
|
|||||||
prepare_dataset,
|
prepare_dataset,
|
||||||
reset_logger_for_multiprocessing,
|
reset_logger_for_multiprocessing,
|
||||||
run_evaluation,
|
run_evaluation,
|
||||||
|
update_agent_config_for_eval,
|
||||||
)
|
)
|
||||||
from openhands.controller.state.state import State
|
from openhands.controller.state.state import State
|
||||||
from openhands.core.config import (
|
from openhands.core.config import (
|
||||||
@@ -76,6 +77,8 @@ def get_config(
|
|||||||
)
|
)
|
||||||
config.set_llm_config(metadata.llm_config)
|
config.set_llm_config(metadata.llm_config)
|
||||||
agent_config = config.get_agent_config(metadata.agent_class)
|
agent_config = config.get_agent_config(metadata.agent_class)
|
||||||
|
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
agent_config.enable_prompt_extensions = False
|
agent_config.enable_prompt_extensions = False
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -160,6 +160,26 @@ def cleanup():
|
|||||||
process.join()
|
process.join()
|
||||||
|
|
||||||
|
|
||||||
|
def update_agent_config_for_eval(
|
||||||
|
agent_config: AgentConfig | None = None,
|
||||||
|
) -> AgentConfig:
|
||||||
|
"""Update agent config with evaluation-specific settings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_config: The agent config to update. If None, a new AgentConfig will be created.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The updated agent config.
|
||||||
|
"""
|
||||||
|
if agent_config is None:
|
||||||
|
agent_config = AgentConfig()
|
||||||
|
|
||||||
|
# Note: We're not disabling repository memory here as requested
|
||||||
|
# This function can be used for other evaluation-specific settings
|
||||||
|
|
||||||
|
return agent_config
|
||||||
|
|
||||||
|
|
||||||
def make_metadata(
|
def make_metadata(
|
||||||
llm_config: LLMConfig,
|
llm_config: LLMConfig,
|
||||||
dataset_name: str,
|
dataset_name: str,
|
||||||
@@ -172,6 +192,8 @@ def make_metadata(
|
|||||||
agent_config: AgentConfig | None = None,
|
agent_config: AgentConfig | None = None,
|
||||||
condenser_config: CondenserConfig | None = None,
|
condenser_config: CondenserConfig | None = None,
|
||||||
) -> EvalMetadata:
|
) -> EvalMetadata:
|
||||||
|
# Update agent config with evaluation-specific settings
|
||||||
|
agent_config = update_agent_config_for_eval(agent_config)
|
||||||
model_name = llm_config.model.split('/')[-1]
|
model_name = llm_config.model.split('/')[-1]
|
||||||
model_path = model_name.replace(':', '_').replace('@', '-')
|
model_path = model_name.replace(':', '_').replace('@', '-')
|
||||||
eval_note = f'_N_{eval_note}' if eval_note else ''
|
eval_note = f'_N_{eval_note}' if eval_note else ''
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
# Run frontend checks
|
|
||||||
echo "Running frontend checks..."
|
|
||||||
cd frontend
|
cd frontend
|
||||||
npm run check-unlocalized-strings
|
npm run check-unlocalized-strings
|
||||||
npx lint-staged
|
npx lint-staged
|
||||||
|
|
||||||
# Run backend pre-commit
|
|
||||||
echo "Running backend pre-commit..."
|
|
||||||
cd ..
|
|
||||||
pre-commit run --files openhands/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
|
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ describe("ChatInput", () => {
|
|||||||
render(<ChatInput onSubmit={onSubmitMock} />);
|
render(<ChatInput onSubmit={onSubmitMock} />);
|
||||||
const textarea = screen.getByRole("textbox");
|
const textarea = screen.getByRole("textbox");
|
||||||
expect(textarea).toBeInTheDocument();
|
expect(textarea).toBeInTheDocument();
|
||||||
|
|
||||||
// The actual verification of maxRows=16 is handled internally by the TextareaAutosize component
|
// The actual verification of maxRows=16 is handled internally by the TextareaAutosize component
|
||||||
// and affects how many rows the textarea can expand to
|
// and affects how many rows the textarea can expand to
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { Message } from "#/message";
|
||||||
import { act, screen, waitFor, within } from "@testing-library/react";
|
import { act, screen, waitFor, within } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { renderWithProviders } from "test-utils";
|
import { renderWithProviders } from "test-utils";
|
||||||
import type { Message } from "#/message";
|
|
||||||
import { addUserMessage } from "#/state/chat-slice";
|
import { addUserMessage } from "#/state/chat-slice";
|
||||||
import { SUGGESTIONS } from "#/utils/suggestions";
|
import { SUGGESTIONS } from "#/utils/suggestions";
|
||||||
import * as ChatSlice from "#/state/chat-slice";
|
import * as ChatSlice from "#/state/chat-slice";
|
||||||
@@ -45,15 +45,7 @@ describe("Empty state", () => {
|
|||||||
it("should render suggestions if empty", () => {
|
it("should render suggestions if empty", () => {
|
||||||
const { store } = renderWithProviders(<ChatInterface />, {
|
const { store } = renderWithProviders(<ChatInterface />, {
|
||||||
preloadedState: {
|
preloadedState: {
|
||||||
chat: {
|
chat: { messages: [] },
|
||||||
messages: [],
|
|
||||||
systemMessage: {
|
|
||||||
content: "",
|
|
||||||
tools: [],
|
|
||||||
openhands_version: null,
|
|
||||||
agent_class: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,15 +68,7 @@ describe("Empty state", () => {
|
|||||||
it("should render the default suggestions", () => {
|
it("should render the default suggestions", () => {
|
||||||
renderWithProviders(<ChatInterface />, {
|
renderWithProviders(<ChatInterface />, {
|
||||||
preloadedState: {
|
preloadedState: {
|
||||||
chat: {
|
chat: { messages: [] },
|
||||||
messages: [],
|
|
||||||
systemMessage: {
|
|
||||||
content: "",
|
|
||||||
tools: [],
|
|
||||||
openhands_version: null,
|
|
||||||
agent_class: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,15 +98,7 @@ describe("Empty state", () => {
|
|||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const { store } = renderWithProviders(<ChatInterface />, {
|
const { store } = renderWithProviders(<ChatInterface />, {
|
||||||
preloadedState: {
|
preloadedState: {
|
||||||
chat: {
|
chat: { messages: [] },
|
||||||
messages: [],
|
|
||||||
systemMessage: {
|
|
||||||
content: "",
|
|
||||||
tools: [],
|
|
||||||
openhands_version: null,
|
|
||||||
agent_class: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -151,15 +127,7 @@ describe("Empty state", () => {
|
|||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const { rerender } = renderWithProviders(<ChatInterface />, {
|
const { rerender } = renderWithProviders(<ChatInterface />, {
|
||||||
preloadedState: {
|
preloadedState: {
|
||||||
chat: {
|
chat: { messages: [] },
|
||||||
messages: [],
|
|
||||||
systemMessage: {
|
|
||||||
content: "",
|
|
||||||
tools: [],
|
|
||||||
openhands_version: null,
|
|
||||||
agent_class: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -95,23 +95,6 @@ describe("ExpandableMessage", () => {
|
|||||||
expect(screen.queryByTestId("status-icon")).not.toBeInTheDocument();
|
expect(screen.queryByTestId("status-icon")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render with neutral border and no icon for action messages with undefined success (timeout case)", () => {
|
|
||||||
renderWithProviders(
|
|
||||||
<ExpandableMessage
|
|
||||||
id="OBSERVATION_MESSAGE$RUN"
|
|
||||||
message="Command timed out"
|
|
||||||
type="action"
|
|
||||||
success={undefined}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
const element = screen.getByText("OBSERVATION_MESSAGE$RUN");
|
|
||||||
const container = element.closest(
|
|
||||||
"div.flex.gap-2.items-center.justify-start",
|
|
||||||
);
|
|
||||||
expect(container).toHaveClass("border-neutral-300");
|
|
||||||
expect(screen.queryByTestId("status-icon")).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the out of credits message when the user is out of credits", async () => {
|
it("should render the out of credits message when the user is out of credits", async () => {
|
||||||
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
|
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
|
||||||
// @ts-expect-error - We only care about the APP_MODE and FEATURE_FLAGS fields
|
// @ts-expect-error - We only care about the APP_MODE and FEATURE_FLAGS fields
|
||||||
|
|||||||
@@ -3,46 +3,34 @@ import { it, describe, expect, vi, beforeAll, afterAll } from "vitest";
|
|||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { AuthModal } from "#/components/features/waitlist/auth-modal";
|
import { AuthModal } from "#/components/features/waitlist/auth-modal";
|
||||||
import * as CaptureConsent from "#/utils/handle-capture-consent";
|
import * as CaptureConsent from "#/utils/handle-capture-consent";
|
||||||
import * as AuthHook from "#/context/auth-context";
|
|
||||||
|
|
||||||
describe("AuthModal", () => {
|
describe("AuthModal", () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
vi.stubGlobal("location", { href: "" });
|
vi.stubGlobal("location", { href: "" });
|
||||||
vi.spyOn(AuthHook, "useAuth").mockReturnValue({
|
|
||||||
providersAreSet: false,
|
|
||||||
setProvidersAreSet: vi.fn(),
|
|
||||||
providerTokensSet: [],
|
|
||||||
setProviderTokensSet: vi.fn()
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
vi.unstubAllGlobals();
|
vi.unstubAllGlobals();
|
||||||
vi.restoreAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render a tos checkbox that is unchecked by default", () => {
|
it("should render a tos checkbox that is unchecked by default", () => {
|
||||||
render(<AuthModal githubAuthUrl={null} appMode="saas" />);
|
render(<AuthModal githubAuthUrl={null} />);
|
||||||
const checkbox = screen.getByRole("checkbox");
|
const checkbox = screen.getByRole("checkbox");
|
||||||
|
|
||||||
expect(checkbox).not.toBeChecked();
|
expect(checkbox).not.toBeChecked();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should only enable the identity provider buttons if the tos checkbox is checked", async () => {
|
it("should only enable the GitHub button if the tos checkbox is checked", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(<AuthModal githubAuthUrl={null} appMode="saas" />);
|
render(<AuthModal githubAuthUrl={null} />);
|
||||||
|
|
||||||
const checkbox = screen.getByRole("checkbox");
|
const checkbox = screen.getByRole("checkbox");
|
||||||
const githubButton = screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" });
|
const button = screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" });
|
||||||
const gitlabButton = screen.getByRole("button", { name: "GITLAB$CONNECT_TO_GITLAB" });
|
|
||||||
|
|
||||||
expect(githubButton).toBeDisabled();
|
expect(button).toBeDisabled();
|
||||||
expect(gitlabButton).toBeDisabled();
|
|
||||||
|
|
||||||
await user.click(checkbox);
|
await user.click(checkbox);
|
||||||
|
|
||||||
expect(githubButton).not.toBeDisabled();
|
expect(button).not.toBeDisabled();
|
||||||
expect(gitlabButton).not.toBeDisabled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set user analytics consent to true when the user checks the tos checkbox", async () => {
|
it("should set user analytics consent to true when the user checks the tos checkbox", async () => {
|
||||||
@@ -52,7 +40,7 @@ describe("AuthModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(<AuthModal githubAuthUrl="mock-url" appMode="saas" />);
|
render(<AuthModal githubAuthUrl="mock-url" />);
|
||||||
|
|
||||||
const checkbox = screen.getByRole("checkbox");
|
const checkbox = screen.getByRole("checkbox");
|
||||||
await user.click(checkbox);
|
await user.click(checkbox);
|
||||||
|
|||||||
@@ -56,16 +56,12 @@ describe("GitRepositorySelector", () => {
|
|||||||
full_name: "test/repo1",
|
full_name: "test/repo1",
|
||||||
git_provider: "github" as Provider,
|
git_provider: "github" as Provider,
|
||||||
stargazers_count: 100,
|
stargazers_count: 100,
|
||||||
is_public: true,
|
|
||||||
pushed_at: "2023-01-01T00:00:00Z",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
full_name: "test/repo2",
|
full_name: "test/repo2",
|
||||||
git_provider: "github" as Provider,
|
git_provider: "github" as Provider,
|
||||||
stargazers_count: 200,
|
stargazers_count: 200,
|
||||||
is_public: true,
|
|
||||||
pushed_at: "2023-01-02T00:00:00Z",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
|
|
||||||
import { render, screen } from "@testing-library/react";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { createRoutesStub } from "react-router";
|
|
||||||
import { setupStore } from "test-utils";
|
|
||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
import { AuthProvider } from "#/context/auth-context";
|
|
||||||
import { HomeHeader } from "#/components/features/home/home-header";
|
|
||||||
import OpenHands from "#/api/open-hands";
|
|
||||||
|
|
||||||
const renderHomeHeader = () => {
|
|
||||||
const RouterStub = createRoutesStub([
|
|
||||||
{
|
|
||||||
Component: HomeHeader,
|
|
||||||
path: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Component: () => <div data-testid="conversation-screen" />,
|
|
||||||
path: "/conversations/:conversationId",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return render(<RouterStub />, {
|
|
||||||
wrapper: ({ children }) => (
|
|
||||||
<Provider store={setupStore()}>
|
|
||||||
<AuthProvider initialProvidersAreSet>
|
|
||||||
<QueryClientProvider client={new QueryClient()}>
|
|
||||||
{children}
|
|
||||||
</QueryClientProvider>
|
|
||||||
</AuthProvider>
|
|
||||||
</Provider>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("HomeHeader", () => {
|
|
||||||
it("should create an empty conversation and redirect when pressing the launch from scratch button", async () => {
|
|
||||||
const createConversationSpy = vi.spyOn(OpenHands, "createConversation");
|
|
||||||
|
|
||||||
renderHomeHeader();
|
|
||||||
|
|
||||||
const launchButton = screen.getByRole("button", {
|
|
||||||
name: /launch from scratch/i,
|
|
||||||
});
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
expect(createConversationSpy).toHaveBeenCalledExactlyOnceWith(
|
|
||||||
"gui",
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
[],
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
// expect to be redirected to /conversations/:conversationId
|
|
||||||
await screen.findByTestId("conversation-screen");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should change the launch button text to 'Loading...' when creating a conversation", async () => {
|
|
||||||
renderHomeHeader();
|
|
||||||
|
|
||||||
const launchButton = screen.getByRole("button", {
|
|
||||||
name: /launch from scratch/i,
|
|
||||||
});
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
expect(launchButton).toHaveTextContent(/Loading/i);
|
|
||||||
expect(launchButton).toBeDisabled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
import { render, screen, waitFor, within } from "@testing-library/react";
|
|
||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
|
|
||||||
import { setupStore } from "test-utils";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { createRoutesStub } from "react-router";
|
|
||||||
import OpenHands from "#/api/open-hands";
|
|
||||||
import { AuthProvider } from "#/context/auth-context";
|
|
||||||
import { GitRepository } from "#/types/git";
|
|
||||||
import * as GitService from "#/api/git";
|
|
||||||
import { RepoConnector } from "#/components/features/home/repo-connector";
|
|
||||||
|
|
||||||
const renderRepoConnector = (initialProvidersAreSet = true) => {
|
|
||||||
const mockRepoSelection = vi.fn();
|
|
||||||
const RouterStub = createRoutesStub([
|
|
||||||
{
|
|
||||||
Component: () => <RepoConnector onRepoSelection={mockRepoSelection} />,
|
|
||||||
path: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Component: () => <div data-testid="conversation-screen" />,
|
|
||||||
path: "/conversations/:conversationId",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Component: () => <div data-testid="settings-screen" />,
|
|
||||||
path: "/settings",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return render(<RouterStub />, {
|
|
||||||
wrapper: ({ children }) => (
|
|
||||||
<Provider store={setupStore()}>
|
|
||||||
<AuthProvider initialProvidersAreSet={initialProvidersAreSet}>
|
|
||||||
<QueryClientProvider client={new QueryClient()}>
|
|
||||||
{children}
|
|
||||||
</QueryClientProvider>
|
|
||||||
</AuthProvider>
|
|
||||||
</Provider>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOCK_RESPOSITORIES: GitRepository[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
full_name: "rbren/polaris",
|
|
||||||
git_provider: "github",
|
|
||||||
is_public: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
full_name: "All-Hands-AI/OpenHands",
|
|
||||||
git_provider: "github",
|
|
||||||
is_public: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
describe("RepoConnector", () => {
|
|
||||||
it("should render the repository connector section", () => {
|
|
||||||
renderRepoConnector();
|
|
||||||
screen.getByTestId("repo-connector");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the available repositories in the dropdown", async () => {
|
|
||||||
const retrieveUserGitRepositoriesSpy = vi.spyOn(
|
|
||||||
GitService,
|
|
||||||
"retrieveUserGitRepositories",
|
|
||||||
);
|
|
||||||
retrieveUserGitRepositoriesSpy.mockResolvedValue({
|
|
||||||
data: MOCK_RESPOSITORIES,
|
|
||||||
nextPage: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderRepoConnector();
|
|
||||||
|
|
||||||
// Wait for the loading state to be replaced with the dropdown
|
|
||||||
const dropdown = await waitFor(() => screen.getByTestId("repo-dropdown"));
|
|
||||||
await userEvent.click(dropdown);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
screen.getByText("rbren/polaris");
|
|
||||||
screen.getByText("All-Hands-AI/OpenHands");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only enable the launch button if a repo is selected", async () => {
|
|
||||||
const retrieveUserGitRepositoriesSpy = vi.spyOn(
|
|
||||||
GitService,
|
|
||||||
"retrieveUserGitRepositories",
|
|
||||||
);
|
|
||||||
retrieveUserGitRepositoriesSpy.mockResolvedValue({
|
|
||||||
data: MOCK_RESPOSITORIES,
|
|
||||||
nextPage: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderRepoConnector();
|
|
||||||
|
|
||||||
const launchButton = screen.getByTestId("repo-launch-button");
|
|
||||||
expect(launchButton).toBeDisabled();
|
|
||||||
|
|
||||||
// Wait for the loading state to be replaced with the dropdown
|
|
||||||
const dropdown = await waitFor(() => screen.getByTestId("repo-dropdown"));
|
|
||||||
await userEvent.click(dropdown);
|
|
||||||
await userEvent.click(screen.getByText("rbren/polaris"));
|
|
||||||
|
|
||||||
expect(launchButton).toBeEnabled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the 'add git(hub|lab) repos' links if saas mode", async () => {
|
|
||||||
const getConfiSpy = vi.spyOn(OpenHands, "getConfig");
|
|
||||||
// @ts-expect-error - only return the APP_MODE
|
|
||||||
getConfiSpy.mockResolvedValue({
|
|
||||||
APP_MODE: "saas",
|
|
||||||
});
|
|
||||||
|
|
||||||
renderRepoConnector();
|
|
||||||
|
|
||||||
await screen.findByText("Add GitHub repos");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not render the 'add git(hub|lab) repos' links if oss mode", async () => {
|
|
||||||
const getConfiSpy = vi.spyOn(OpenHands, "getConfig");
|
|
||||||
// @ts-expect-error - only return the APP_MODE
|
|
||||||
getConfiSpy.mockResolvedValue({
|
|
||||||
APP_MODE: "oss",
|
|
||||||
});
|
|
||||||
|
|
||||||
renderRepoConnector();
|
|
||||||
|
|
||||||
expect(screen.queryByText("Add GitHub repos")).not.toBeInTheDocument();
|
|
||||||
expect(screen.queryByText("Add GitLab repos")).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create a conversation and redirect with the selected repo when pressing the launch button", async () => {
|
|
||||||
const createConversationSpy = vi.spyOn(OpenHands, "createConversation");
|
|
||||||
const retrieveUserGitRepositoriesSpy = vi.spyOn(
|
|
||||||
GitService,
|
|
||||||
"retrieveUserGitRepositories",
|
|
||||||
);
|
|
||||||
retrieveUserGitRepositoriesSpy.mockResolvedValue({
|
|
||||||
data: MOCK_RESPOSITORIES,
|
|
||||||
nextPage: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderRepoConnector();
|
|
||||||
|
|
||||||
const repoConnector = screen.getByTestId("repo-connector");
|
|
||||||
const launchButton =
|
|
||||||
within(repoConnector).getByTestId("repo-launch-button");
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
// repo not selected yet
|
|
||||||
expect(createConversationSpy).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// select a repository from the dropdown
|
|
||||||
const dropdown = await waitFor(() =>
|
|
||||||
within(repoConnector).getByTestId("repo-dropdown"),
|
|
||||||
);
|
|
||||||
await userEvent.click(dropdown);
|
|
||||||
|
|
||||||
const repoOption = screen.getByText("rbren/polaris");
|
|
||||||
await userEvent.click(repoOption);
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
expect(createConversationSpy).toHaveBeenCalledExactlyOnceWith(
|
|
||||||
"gui",
|
|
||||||
{
|
|
||||||
full_name: "rbren/polaris",
|
|
||||||
git_provider: "github",
|
|
||||||
id: 1,
|
|
||||||
is_public: true,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
[],
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should change the launch button text to 'Loading...' when creating a conversation", async () => {
|
|
||||||
const retrieveUserGitRepositoriesSpy = vi.spyOn(
|
|
||||||
GitService,
|
|
||||||
"retrieveUserGitRepositories",
|
|
||||||
);
|
|
||||||
retrieveUserGitRepositoriesSpy.mockResolvedValue({
|
|
||||||
data: MOCK_RESPOSITORIES,
|
|
||||||
nextPage: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderRepoConnector();
|
|
||||||
|
|
||||||
const launchButton = screen.getByTestId("repo-launch-button");
|
|
||||||
|
|
||||||
// Wait for the loading state to be replaced with the dropdown
|
|
||||||
const dropdown = await waitFor(() => screen.getByTestId("repo-dropdown"));
|
|
||||||
await userEvent.click(dropdown);
|
|
||||||
await userEvent.click(screen.getByText("rbren/polaris"));
|
|
||||||
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
expect(launchButton).toBeDisabled();
|
|
||||||
expect(launchButton).toHaveTextContent(/Loading/i);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not display a button to settings if the user is signed in with their git provider", async () => {
|
|
||||||
renderRepoConnector(true);
|
|
||||||
expect(
|
|
||||||
screen.queryByTestId("navigate-to-settings-button"),
|
|
||||||
).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display a button to settings if the user needs to sign in with their git provider", async () => {
|
|
||||||
renderRepoConnector(false);
|
|
||||||
|
|
||||||
const goToSettingsButton = await screen.findByTestId(
|
|
||||||
"navigate-to-settings-button",
|
|
||||||
);
|
|
||||||
const dropdown = screen.queryByTestId("repo-dropdown");
|
|
||||||
const launchButton = screen.queryByTestId("repo-launch-button");
|
|
||||||
const providerLinks = screen.queryAllByText(/add git(hub|lab) repos/i);
|
|
||||||
|
|
||||||
expect(dropdown).not.toBeInTheDocument();
|
|
||||||
expect(launchButton).not.toBeInTheDocument();
|
|
||||||
expect(providerLinks.length).toBe(0);
|
|
||||||
|
|
||||||
expect(goToSettingsButton).toBeInTheDocument();
|
|
||||||
|
|
||||||
await userEvent.click(goToSettingsButton);
|
|
||||||
await screen.findByTestId("settings-screen");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
import { render, screen } from "@testing-library/react";
|
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { createRoutesStub } from "react-router";
|
|
||||||
import { setupStore } from "test-utils";
|
|
||||||
import { SuggestedTask } from "#/components/features/home/tasks/task.types";
|
|
||||||
import OpenHands from "#/api/open-hands";
|
|
||||||
import { AuthProvider } from "#/context/auth-context";
|
|
||||||
import { TaskCard } from "#/components/features/home/tasks/task-card";
|
|
||||||
import * as GitService from "#/api/git";
|
|
||||||
import { GitRepository } from "#/types/git";
|
|
||||||
|
|
||||||
const MOCK_TASK_1: SuggestedTask = {
|
|
||||||
issue_number: 123,
|
|
||||||
repo: "repo1",
|
|
||||||
title: "Task 1",
|
|
||||||
task_type: "MERGE_CONFLICTS",
|
|
||||||
git_provider: "github",
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOCK_TASK_2: SuggestedTask = {
|
|
||||||
issue_number: 456,
|
|
||||||
repo: "repo2",
|
|
||||||
title: "Task 2",
|
|
||||||
task_type: "FAILING_CHECKS",
|
|
||||||
git_provider: "github",
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOCK_TASK_3: SuggestedTask = {
|
|
||||||
issue_number: 789,
|
|
||||||
repo: "repo3",
|
|
||||||
title: "Task 3",
|
|
||||||
task_type: "UNRESOLVED_COMMENTS",
|
|
||||||
git_provider: "gitlab",
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOCK_TASK_4: SuggestedTask = {
|
|
||||||
issue_number: 101112,
|
|
||||||
repo: "repo4",
|
|
||||||
title: "Task 4",
|
|
||||||
task_type: "OPEN_ISSUE",
|
|
||||||
git_provider: "gitlab",
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOCK_RESPOSITORIES: GitRepository[] = [
|
|
||||||
{ id: 1, full_name: "repo1", git_provider: "github", is_public: true },
|
|
||||||
{ id: 2, full_name: "repo2", git_provider: "github", is_public: true },
|
|
||||||
{ id: 3, full_name: "repo3", git_provider: "gitlab", is_public: true },
|
|
||||||
{ id: 4, full_name: "repo4", git_provider: "gitlab", is_public: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const renderTaskCard = (task = MOCK_TASK_1) => {
|
|
||||||
const RouterStub = createRoutesStub([
|
|
||||||
{
|
|
||||||
Component: () => <TaskCard task={task} />,
|
|
||||||
path: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Component: () => <div data-testid="conversation-screen" />,
|
|
||||||
path: "/conversations/:conversationId",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return render(<RouterStub />, {
|
|
||||||
wrapper: ({ children }) => (
|
|
||||||
<Provider store={setupStore()}>
|
|
||||||
<AuthProvider initialProvidersAreSet>
|
|
||||||
<QueryClientProvider client={new QueryClient()}>
|
|
||||||
{children}
|
|
||||||
</QueryClientProvider>
|
|
||||||
</AuthProvider>
|
|
||||||
</Provider>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("TaskCard", () => {
|
|
||||||
it("format the issue id", async () => {
|
|
||||||
renderTaskCard();
|
|
||||||
|
|
||||||
const taskId = screen.getByTestId("task-id");
|
|
||||||
expect(taskId).toHaveTextContent(/#123/i);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call createConversation when clicking the launch button", async () => {
|
|
||||||
const createConversationSpy = vi.spyOn(OpenHands, "createConversation");
|
|
||||||
|
|
||||||
renderTaskCard();
|
|
||||||
|
|
||||||
const launchButton = screen.getByTestId("task-launch-button");
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
expect(createConversationSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("creating suggested task conversation", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const retrieveUserGitRepositoriesSpy = vi.spyOn(
|
|
||||||
GitService,
|
|
||||||
"retrieveUserGitRepositories",
|
|
||||||
);
|
|
||||||
retrieveUserGitRepositoriesSpy.mockResolvedValue({
|
|
||||||
data: MOCK_RESPOSITORIES,
|
|
||||||
nextPage: null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call create conversation with suggest task trigger and selected suggested task", async () => {
|
|
||||||
const createConversationSpy = vi.spyOn(OpenHands, "createConversation");
|
|
||||||
|
|
||||||
renderTaskCard(MOCK_TASK_1);
|
|
||||||
|
|
||||||
const launchButton = screen.getByTestId("task-launch-button");
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
expect(createConversationSpy).toHaveBeenCalledWith(
|
|
||||||
"suggested_task",
|
|
||||||
MOCK_RESPOSITORIES[0],
|
|
||||||
undefined,
|
|
||||||
[],
|
|
||||||
undefined,
|
|
||||||
MOCK_TASK_1,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should disable the launch button and update text content when creating a conversation", async () => {
|
|
||||||
renderTaskCard();
|
|
||||||
|
|
||||||
const launchButton = screen.getByTestId("task-launch-button");
|
|
||||||
await userEvent.click(launchButton);
|
|
||||||
|
|
||||||
expect(launchButton).toHaveTextContent(/Loading/i);
|
|
||||||
expect(launchButton).toBeDisabled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import { render, screen, waitFor } from "@testing-library/react";
|
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { createRoutesStub } from "react-router";
|
|
||||||
import { setupStore } from "test-utils";
|
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
import { TaskSuggestions } from "#/components/features/home/tasks/task-suggestions";
|
|
||||||
import { SuggestionsService } from "#/api/suggestions-service/suggestions-service.api";
|
|
||||||
import { MOCK_TASKS } from "#/mocks/task-suggestions-handlers";
|
|
||||||
import { AuthProvider } from "#/context/auth-context";
|
|
||||||
|
|
||||||
const renderTaskSuggestions = (initialProvidersAreSet = true) => {
|
|
||||||
const RouterStub = createRoutesStub([
|
|
||||||
{
|
|
||||||
Component: TaskSuggestions,
|
|
||||||
path: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Component: () => <div data-testid="conversation-screen" />,
|
|
||||||
path: "/conversations/:conversationId",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Component: () => <div data-testid="settings-screen" />,
|
|
||||||
path: "/settings",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return render(<RouterStub />, {
|
|
||||||
wrapper: ({ children }) => (
|
|
||||||
<Provider store={setupStore()}>
|
|
||||||
<AuthProvider initialProvidersAreSet={initialProvidersAreSet}>
|
|
||||||
<QueryClientProvider client={new QueryClient()}>
|
|
||||||
{children}
|
|
||||||
</QueryClientProvider>
|
|
||||||
</AuthProvider>
|
|
||||||
</Provider>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("TaskSuggestions", () => {
|
|
||||||
const getSuggestedTasksSpy = vi.spyOn(
|
|
||||||
SuggestionsService,
|
|
||||||
"getSuggestedTasks",
|
|
||||||
);
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the task suggestions section", () => {
|
|
||||||
renderTaskSuggestions();
|
|
||||||
screen.getByTestId("task-suggestions");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render an empty message if there are no tasks", async () => {
|
|
||||||
getSuggestedTasksSpy.mockResolvedValue([]);
|
|
||||||
renderTaskSuggestions();
|
|
||||||
await screen.findByText(/No tasks available/i);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the task groups with the correct titles", async () => {
|
|
||||||
getSuggestedTasksSpy.mockResolvedValue(MOCK_TASKS);
|
|
||||||
renderTaskSuggestions();
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
MOCK_TASKS.forEach((taskGroup) => {
|
|
||||||
screen.getByText(taskGroup.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the task cards with the correct task details", async () => {
|
|
||||||
getSuggestedTasksSpy.mockResolvedValue(MOCK_TASKS);
|
|
||||||
renderTaskSuggestions();
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
MOCK_TASKS.forEach((task) => {
|
|
||||||
screen.getByText(task.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render skeletons when loading", async () => {
|
|
||||||
getSuggestedTasksSpy.mockResolvedValue(MOCK_TASKS);
|
|
||||||
renderTaskSuggestions();
|
|
||||||
|
|
||||||
const skeletons = screen.getAllByTestId("task-group-skeleton");
|
|
||||||
expect(skeletons.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
MOCK_TASKS.forEach((taskGroup) => {
|
|
||||||
screen.getByText(taskGroup.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.queryByTestId("task-group-skeleton")).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display a button to settings if the user needs to sign in with their git provider", async () => {
|
|
||||||
renderTaskSuggestions(false);
|
|
||||||
|
|
||||||
expect(getSuggestedTasksSpy).not.toHaveBeenCalled();
|
|
||||||
const goToSettingsButton = await screen.findByTestId(
|
|
||||||
"navigate-to-settings-button",
|
|
||||||
);
|
|
||||||
expect(goToSettingsButton).toBeInTheDocument();
|
|
||||||
|
|
||||||
await userEvent.click(goToSettingsButton);
|
|
||||||
await screen.findByTestId("settings-screen");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -61,25 +61,25 @@ describe("PaymentForm", () => {
|
|||||||
renderPaymentForm();
|
renderPaymentForm();
|
||||||
|
|
||||||
const topUpInput = await screen.findByTestId("top-up-input");
|
const topUpInput = await screen.findByTestId("top-up-input");
|
||||||
await user.type(topUpInput, "50");
|
await user.type(topUpInput, "50.12");
|
||||||
|
|
||||||
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
||||||
await user.click(topUpButton);
|
await user.click(topUpButton);
|
||||||
|
|
||||||
expect(createCheckoutSessionSpy).toHaveBeenCalledWith(50);
|
expect(createCheckoutSessionSpy).toHaveBeenCalledWith(50.12);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should only accept integer values", async () => {
|
it("should round the top-up amount to two decimal places", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
renderPaymentForm();
|
renderPaymentForm();
|
||||||
|
|
||||||
const topUpInput = await screen.findByTestId("top-up-input");
|
const topUpInput = await screen.findByTestId("top-up-input");
|
||||||
await user.type(topUpInput, "50");
|
await user.type(topUpInput, "50.125456");
|
||||||
|
|
||||||
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
||||||
await user.click(topUpButton);
|
await user.click(topUpButton);
|
||||||
|
|
||||||
expect(createCheckoutSessionSpy).toHaveBeenCalledWith(50);
|
expect(createCheckoutSessionSpy).toHaveBeenCalledWith(50.13);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disable the top-up button if the user enters an invalid amount", async () => {
|
it("should disable the top-up button if the user enters an invalid amount", async () => {
|
||||||
@@ -100,7 +100,7 @@ describe("PaymentForm", () => {
|
|||||||
renderPaymentForm();
|
renderPaymentForm();
|
||||||
|
|
||||||
const topUpInput = await screen.findByTestId("top-up-input");
|
const topUpInput = await screen.findByTestId("top-up-input");
|
||||||
await user.type(topUpInput, "50");
|
await user.type(topUpInput, "50.12");
|
||||||
|
|
||||||
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
||||||
await user.click(topUpButton);
|
await user.click(topUpButton);
|
||||||
@@ -114,7 +114,7 @@ describe("PaymentForm", () => {
|
|||||||
renderPaymentForm();
|
renderPaymentForm();
|
||||||
|
|
||||||
const topUpInput = await screen.findByTestId("top-up-input");
|
const topUpInput = await screen.findByTestId("top-up-input");
|
||||||
await user.type(topUpInput, "-50");
|
await user.type(topUpInput, "-50.12");
|
||||||
|
|
||||||
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
||||||
await user.click(topUpButton);
|
await user.click(topUpButton);
|
||||||
@@ -139,8 +139,6 @@ describe("PaymentForm", () => {
|
|||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
renderPaymentForm();
|
renderPaymentForm();
|
||||||
|
|
||||||
// With type="number", the browser would prevent non-numeric input,
|
|
||||||
// but we'll test the validation logic anyway
|
|
||||||
const topUpInput = await screen.findByTestId("top-up-input");
|
const topUpInput = await screen.findByTestId("top-up-input");
|
||||||
await user.type(topUpInput, "abc");
|
await user.type(topUpInput, "abc");
|
||||||
|
|
||||||
@@ -162,19 +160,5 @@ describe("PaymentForm", () => {
|
|||||||
|
|
||||||
expect(createCheckoutSessionSpy).not.toHaveBeenCalled();
|
expect(createCheckoutSessionSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("user enters a decimal value", async () => {
|
|
||||||
const user = userEvent.setup();
|
|
||||||
renderPaymentForm();
|
|
||||||
|
|
||||||
// With step="1", the browser would validate this, but we'll test our validation logic
|
|
||||||
const topUpInput = await screen.findByTestId("top-up-input");
|
|
||||||
await user.type(topUpInput, "50.5");
|
|
||||||
|
|
||||||
const topUpButton = screen.getByText("PAYMENT$ADD_CREDIT");
|
|
||||||
await user.click(topUpButton);
|
|
||||||
|
|
||||||
expect(createCheckoutSessionSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user