mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 030e08b68f | |||
| 92a8bcd037 |
@@ -19,6 +19,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
@@ -41,6 +41,20 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
with:
|
||||
@@ -90,6 +104,20 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
with:
|
||||
@@ -191,7 +219,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run unit tests with the Docker runtime Docker images as root
|
||||
# Run unit tests with the EventStream runtime Docker images as root
|
||||
test_runtime_root:
|
||||
name: RT Unit Tests (Root)
|
||||
needs: [ghcr_build_runtime]
|
||||
@@ -202,6 +230,20 @@ jobs:
|
||||
base_image: ['nikolaik']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -244,7 +286,7 @@ jobs:
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image }}
|
||||
image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
TEST_RUNTIME=docker \
|
||||
TEST_RUNTIME=eventstream \
|
||||
SANDBOX_USER_ID=$(id -u) \
|
||||
SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
|
||||
TEST_IN_CI=true \
|
||||
@@ -255,7 +297,7 @@ jobs:
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
# Run unit tests with the Docker runtime Docker images as openhands user
|
||||
# Run unit tests with the EventStream runtime Docker images as openhands user
|
||||
test_runtime_oh:
|
||||
name: RT Unit Tests (openhands)
|
||||
runs-on: ubuntu-latest
|
||||
@@ -265,6 +307,20 @@ jobs:
|
||||
base_image: ['nikolaik']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -307,7 +363,7 @@ jobs:
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image }}
|
||||
image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
TEST_RUNTIME=docker \
|
||||
TEST_RUNTIME=eventstream \
|
||||
SANDBOX_USER_ID=$(id -u) \
|
||||
SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
|
||||
TEST_IN_CI=true \
|
||||
|
||||
@@ -20,10 +20,6 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: "anthropic/claude-3-5-sonnet-20241022"
|
||||
LLM_API_VERSION:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
base_container_image:
|
||||
required: false
|
||||
type: string
|
||||
@@ -120,7 +116,6 @@ jobs:
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || inputs.LLM_MODEL }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
LLM_API_VERSION: ${{ inputs.LLM_API_VERSION }}
|
||||
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
PAT_USERNAME: ${{ secrets.PAT_USERNAME }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
@@ -235,7 +230,6 @@ jobs:
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || inputs.LLM_MODEL }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
LLM_API_VERSION: ${{ inputs.LLM_API_VERSION }}
|
||||
PYTHONPATH: ""
|
||||
run: |
|
||||
cd /tmp && python -m openhands.resolver.resolve_issue \
|
||||
@@ -271,7 +265,6 @@ jobs:
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || inputs.LLM_MODEL }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
LLM_API_VERSION: ${{ inputs.LLM_API_VERSION }}
|
||||
PYTHONPATH: ""
|
||||
run: |
|
||||
if [ "${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}" == "true" ]; then
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Workflow that runs python unit tests on mac
|
||||
name: Run Python Unit Tests Mac
|
||||
|
||||
# This job is flaky so only run it nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
# Run python unit tests on macOS
|
||||
test-on-macos:
|
||||
name: Python Unit Tests on macOS
|
||||
runs-on: macos-14
|
||||
env:
|
||||
INSTALL_DOCKER: '1' # Set to '0' to skip Docker installation
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.12']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pypoetry
|
||||
~/.virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
- name: Install tmux
|
||||
run: brew install tmux
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation,llama-index
|
||||
- name: Install & Start Docker
|
||||
if: env.INSTALL_DOCKER == '1'
|
||||
run: |
|
||||
INSTANCE_NAME="colima-${GITHUB_RUN_ID}"
|
||||
|
||||
# Uninstall colima to upgrade to the latest version
|
||||
if brew list colima &>/dev/null; then
|
||||
brew uninstall colima
|
||||
# unlinking colima dependency: go
|
||||
brew uninstall go@1.21
|
||||
fi
|
||||
rm -rf ~/.colima ~/.lima
|
||||
brew install --HEAD colima
|
||||
brew install docker
|
||||
|
||||
start_colima() {
|
||||
# Find a free port in the range 10000-20000
|
||||
RANDOM_PORT=$((RANDOM % 10001 + 10000))
|
||||
|
||||
# Original line:
|
||||
if ! colima start --network-address --arch x86_64 --cpu=1 --memory=1 --verbose --ssh-port $RANDOM_PORT; then
|
||||
echo "Failed to start Colima."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Attempt to start Colima for 5 total attempts:
|
||||
ATTEMPT_LIMIT=5
|
||||
for ((i=1; i<=ATTEMPT_LIMIT; i++)); do
|
||||
|
||||
if start_colima; then
|
||||
echo "Colima started successfully."
|
||||
break
|
||||
else
|
||||
colima stop -f
|
||||
sleep 10
|
||||
colima delete -f
|
||||
if [ $i -eq $ATTEMPT_LIMIT ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
# For testcontainers to find the Colima socket
|
||||
# https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running
|
||||
sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Run Tests
|
||||
run: poetry run pytest --forked --cov=openhands --cov-report=xml ./tests/unit --ignore=tests/unit/test_memory.py
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -19,4 +19,3 @@ jobs:
|
||||
close-issue-message: 'This issue was closed because it has been stalled for over 30 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for over 30 days with no activity.'
|
||||
days-before-close: 7
|
||||
operations-per-run: 150
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ poetry run pytest ./tests/unit/test_*.py
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker container image by
|
||||
setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.23-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.21-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -43,17 +43,17 @@ See the [Running OpenHands](https://docs.all-hands.dev/modules/usage/installatio
|
||||
system requirements and more information.
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21
|
||||
```
|
||||
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.23-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.21-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ services:
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of openhands-state for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
# 📦 Runtime Docker
|
||||
# 📦 Runtime EventStream
|
||||
|
||||
Le Runtime Docker d'OpenHands est le composant principal qui permet l'exécution sécurisée et flexible des actions des agents d'IA.
|
||||
Le Runtime EventStream d'OpenHands est le composant principal qui permet l'exécution sécurisée et flexible des actions des agents d'IA.
|
||||
Il crée un environnement en bac à sable (sandbox) en utilisant Docker, où du code arbitraire peut être exécuté en toute sécurité sans risquer le système hôte.
|
||||
|
||||
## Pourquoi avons-nous besoin d'un runtime en bac à sable ?
|
||||
|
||||
@@ -163,7 +163,7 @@ Les options de configuration de base sont définies dans la section `[core]` du
|
||||
|
||||
- `runtime`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"docker"`
|
||||
- Valeur par défaut : `"eventstream"`
|
||||
- Description : Environnement d'exécution
|
||||
|
||||
- `default_agent`
|
||||
|
||||
@@ -52,7 +52,7 @@ LLM_API_KEY="sk_test_12345"
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -61,7 +61,7 @@ docker run -it \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.cli
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ Pour créer un workflow d'évaluation pour votre benchmark, suivez ces étapes :
|
||||
def get_config(instance: pd.Series, metadata: EvalMetadata) -> AppConfig:
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
runtime='docker',
|
||||
runtime='eventstream',
|
||||
max_iterations=metadata.max_iterations,
|
||||
sandbox=SandboxConfig(
|
||||
base_container_image='your_container_image',
|
||||
|
||||
@@ -46,7 +46,7 @@ LLM_API_KEY="sk_test_12345"
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -56,6 +56,6 @@ docker run -it \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
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.
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21
|
||||
```
|
||||
|
||||
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 # ...
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
# ...
|
||||
```
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
以下是翻译后的内容:
|
||||
|
||||
# 📦 Docker 运行时
|
||||
# 📦 EventStream 运行时
|
||||
|
||||
OpenHands Docker 运行时是实现 AI 代理操作安全灵活执行的核心组件。
|
||||
OpenHands EventStream 运行时是实现 AI 代理操作安全灵活执行的核心组件。
|
||||
它使用 Docker 创建一个沙盒环境,可以安全地运行任意代码而不会危及主机系统。
|
||||
|
||||
## 为什么我们需要沙盒运行时?
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@
|
||||
|
||||
- `runtime`
|
||||
- 类型: `str`
|
||||
- 默认值: `"docker"`
|
||||
- 默认值: `"eventstream"`
|
||||
- 描述: 运行时环境
|
||||
|
||||
- `default_agent`
|
||||
|
||||
@@ -50,7 +50,7 @@ LLM_API_KEY="sk_test_12345"
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -59,7 +59,7 @@ docker run -it \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.cli
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ OpenHands 的主要入口点在 `openhands/core/main.py` 中。以下是它的
|
||||
def get_config(instance: pd.Series, metadata: EvalMetadata) -> AppConfig:
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
runtime='docker',
|
||||
runtime='eventstream',
|
||||
max_iterations=metadata.max_iterations,
|
||||
sandbox=SandboxConfig(
|
||||
base_container_image='your_container_image',
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ LLM_API_KEY="sk_test_12345"
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -57,6 +57,6 @@ docker run -it \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
|
||||
```
|
||||
|
||||
@@ -11,16 +11,16 @@
|
||||
在 Docker 中运行 OpenHands 是最简单的方式。
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21
|
||||
```
|
||||
|
||||
你也可以在可脚本化的[无头模式](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 # ...
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 📦 Docker Runtime
|
||||
# 📦 EventStream Runtime
|
||||
|
||||
The OpenHands Docker Runtime is the core component that enables secure and flexible execution of AI agent's action.
|
||||
The OpenHands EventStream Runtime is the core component that enables secure and flexible execution of AI agent's action.
|
||||
It creates a sandboxed environment using Docker, where arbitrary code can be run safely without risking the host system.
|
||||
|
||||
## Why do we need a sandboxed runtime?
|
||||
|
||||
@@ -126,7 +126,7 @@ The core configuration options are defined in the `[core]` section of the `confi
|
||||
|
||||
- `runtime`
|
||||
- Type: `str`
|
||||
- Default: `"docker"`
|
||||
- Default: `"eventstream"`
|
||||
- Description: Runtime environment
|
||||
|
||||
- `default_agent`
|
||||
|
||||
@@ -35,7 +35,7 @@ To run OpenHands in CLI mode with Docker:
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -45,7 +45,7 @@ docker run -it \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.cli
|
||||
```
|
||||
|
||||
|
||||
@@ -41,16 +41,8 @@ docker build -t custom-image .
|
||||
|
||||
This will produce a new image called `custom-image`, which will be available in Docker.
|
||||
|
||||
## Using the Docker Command
|
||||
|
||||
When running OpenHands using [the docker command](/modules/usage/installation#start-the-app), replace
|
||||
`-e SANDBOX_RUNTIME_CONTAINER_IMAGE=...` with `-e SANDBOX_BASE_CONTAINER_IMAGE=<custom image name>`:
|
||||
|
||||
```commandline
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_BASE_CONTAINER_IMAGE=custom-image \
|
||||
...
|
||||
```
|
||||
> Note that in the configuration described in this document, OpenHands will run as user "openhands" inside the
|
||||
> sandbox and thus all packages installed via the docker file should be available to all users on the system, not just root.
|
||||
|
||||
## Using the Development Workflow
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ To create an evaluation workflow for your benchmark, follow these steps:
|
||||
def get_config(instance: pd.Series, metadata: EvalMetadata) -> AppConfig:
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
runtime='docker',
|
||||
runtime='eventstream',
|
||||
max_iterations=metadata.max_iterations,
|
||||
sandbox=SandboxConfig(
|
||||
base_container_image='your_container_image',
|
||||
|
||||
@@ -32,7 +32,7 @@ To run OpenHands in Headless mode with Docker:
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -43,7 +43,7 @@ docker run -it \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
|
||||
@@ -43,10 +43,6 @@
|
||||
- General: `Use the WSL 2 based engine` is enabled.
|
||||
- Resources > WSL Integration: `Enable integration with my default WSL distro` is enabled.
|
||||
|
||||
:::note
|
||||
The docker command below to start the app must be run inside the WSL terminal.
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
## Start the App
|
||||
@@ -54,17 +50,17 @@
|
||||
The easiest way to run OpenHands is in Docker.
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.23
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21
|
||||
```
|
||||
|
||||
You'll find OpenHands running at http://localhost:3000!
|
||||
|
||||
@@ -16,7 +16,7 @@ some flags being passed to `docker run` that make this possible:
|
||||
|
||||
```
|
||||
docker run # ...
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.23-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -69,7 +69,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=False,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -53,7 +53,6 @@ def get_config(
|
||||
remote_runtime_api_url=os.environ.get('SANDBOX_REMOTE_RUNTIME_API_URL'),
|
||||
keep_runtime_alive=False,
|
||||
remote_runtime_init_timeout=3600,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -61,7 +61,6 @@ def get_config(
|
||||
remote_runtime_api_url=os.environ.get('SANDBOX_REMOTE_RUNTIME_API_URL'),
|
||||
keep_runtime_alive=False,
|
||||
remote_runtime_init_timeout=1800,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -67,7 +67,6 @@ def get_config(
|
||||
base_container_image=BIOCODER_BENCH_CONTAINER_IMAGE,
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -80,7 +80,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -45,7 +45,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=False,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
workspace_base=None,
|
||||
workspace_mount_path=None,
|
||||
|
||||
@@ -135,7 +135,6 @@ def get_config(
|
||||
remote_runtime_api_url=os.environ.get('SANDBOX_REMOTE_RUNTIME_API_URL'),
|
||||
keep_runtime_alive=False,
|
||||
remote_runtime_init_timeout=3600,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -71,7 +71,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -56,7 +56,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -49,7 +49,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -70,7 +70,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -91,7 +91,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -55,7 +55,6 @@ def get_config(
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
runtime_extra_deps='$OH_INTERPRETER_PATH -m pip install scitools-pyke',
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -70,7 +70,6 @@ def get_config(
|
||||
remote_runtime_init_timeout=1800,
|
||||
keep_runtime_alive=False,
|
||||
timeout=120,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -113,7 +113,6 @@ def get_config(
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
runtime_extra_deps=f'$OH_INTERPRETER_PATH -m pip install {" ".join(MINT_DEPENDENCIES)}',
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -73,7 +73,6 @@ def get_config(
|
||||
api_key=os.environ.get('ALLHANDS_API_KEY', None),
|
||||
remote_runtime_api_url=os.environ.get('SANDBOX_REMOTE_RUNTIME_API_URL'),
|
||||
keep_runtime_alive=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -412,17 +412,6 @@ if __name__ == '__main__':
|
||||
with open(metadata_filepath, 'r') as metadata_file:
|
||||
data = metadata_file.read()
|
||||
metadata = EvalMetadata.model_validate_json(data)
|
||||
else:
|
||||
# Initialize with a dummy metadata when file doesn't exist
|
||||
metadata = EvalMetadata(
|
||||
agent_class="dummy_agent", # Placeholder agent class
|
||||
llm_config=LLMConfig(model="dummy_model"), # Minimal LLM config
|
||||
max_iterations=1, # Minimal iterations
|
||||
eval_output_dir=os.path.dirname(args.input_file), # Use input file dir as output dir
|
||||
start_time=time.strftime('%Y-%m-%d %H:%M:%S'), # Current time
|
||||
git_commit=subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('utf-8').strip(), # Current commit
|
||||
dataset=args.dataset # Dataset name from args
|
||||
)
|
||||
|
||||
# The evaluation harness constrains the signature of `process_instance_func` but we need to
|
||||
# pass extra information. Build a new function object to avoid issues with multiprocessing.
|
||||
|
||||
@@ -139,12 +139,10 @@ def get_config(
|
||||
remote_runtime_api_url=os.environ.get('SANDBOX_REMOTE_RUNTIME_API_URL'),
|
||||
keep_runtime_alive=False,
|
||||
remote_runtime_init_timeout=3600,
|
||||
remote_runtime_api_timeout=120,
|
||||
remote_runtime_resource_factor=get_instance_resource_factor(
|
||||
dataset_name=metadata.dataset,
|
||||
instance_id=instance['instance_id'],
|
||||
),
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -17,14 +17,11 @@ When the `run_infer.sh` script is started, it will automatically pull all task i
|
||||
|
||||
```bash
|
||||
./evaluation/benchmarks/the_agent_company/scripts/run_infer.sh \
|
||||
--agent-llm-config <agent-llm-config, default to 'agent'> \
|
||||
--env-llm-config <env-llm-config, default to 'env'> \
|
||||
--outputs-path <outputs-path, default to outputs> \
|
||||
--server-hostname <server-hostname, default to localhost> \
|
||||
--version <version, default to 1.0.0> \
|
||||
--start-percentile <integer from 0 to 99, default to 0> \
|
||||
--end-percentile <integer from 1 to 100, default to 100>
|
||||
|
||||
--agent-llm-config <agent-llm-config> \
|
||||
--env-llm-config <env-llm-config> \
|
||||
--outputs-path <outputs-path> \
|
||||
--server-hostname <server-hostname> \
|
||||
--version <version>
|
||||
|
||||
# Example
|
||||
./evaluation/benchmarks/the_agent_company/scripts/run_infer.sh \
|
||||
@@ -32,9 +29,7 @@ When the `run_infer.sh` script is started, it will automatically pull all task i
|
||||
--env-llm-config claude-3-5-sonnet-20240620 \
|
||||
--outputs-path outputs \
|
||||
--server-hostname localhost \
|
||||
--version 1.0.0 \
|
||||
--start-percentile 10 \
|
||||
--end-percentile 20
|
||||
--version 1.0.0
|
||||
```
|
||||
|
||||
- `agent-llm-config`: the config name for the agent LLM. This should match the config name in config.toml. This is the LLM used by the agent (e.g. CodeActAgent).
|
||||
@@ -42,11 +37,7 @@ When the `run_infer.sh` script is started, it will automatically pull all task i
|
||||
- `outputs-path`: the path to save trajectories and evaluation results.
|
||||
- `server-hostname`: the hostname of the server that hosts all the web services. It could be localhost if you are running the evaluation and services on the same machine. If the services are hosted on a remote machine, you must use the hostname of the remote machine rather than IP address.
|
||||
- `version`: the version of the task images to use. Currently, the only supported version is 1.0.0.
|
||||
- `start-percentile`: the start percentile of the task split, must be an integer between 0 to 99.
|
||||
- `end-percentile`: the end percentile of the task split, must be an integer between 1 to 100 and larger than start-percentile.
|
||||
|
||||
The script is idempotent. If you run it again, it will resume from the last checkpoint. It would usually take 2 days to finish evaluation if you run the whole task set.
|
||||
To speed up evaluation, you can use `start-percentile` and `end-percentile` to split the tasks for higher parallelism,
|
||||
provided concurrent runs are **targeting different servers**.
|
||||
The script is idempotent. If you run it again, it will resume from the last checkpoint. It would usually take a few days to finish evaluation.
|
||||
|
||||
Note: the script will automatically skip a task if it encounters an error. This usually happens when the OpenHands runtime dies due to some unexpected errors. This means even if the script finishes, it might not have evaluated all tasks. You can manually resume the evaluation by running the script again.
|
||||
|
||||
@@ -50,7 +50,6 @@ def get_config(
|
||||
# large enough timeout, since some testcases take very long to run
|
||||
timeout=300,
|
||||
api_key=os.environ.get('ALLHANDS_API_KEY', None),
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# we mount trajectories path so that trajectories, generated by OpenHands
|
||||
# controller, can be accessible to the evaluator file in the runtime container
|
||||
|
||||
Executable → Regular
+2
-47
@@ -56,14 +56,6 @@ while [[ $# -gt 0 ]]; do
|
||||
VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--start-percentile)
|
||||
START_PERCENTILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--end-percentile)
|
||||
END_PERCENTILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1"
|
||||
exit 1
|
||||
@@ -77,53 +69,16 @@ if [[ ! "$OUTPUTS_PATH" = /* ]]; then
|
||||
OUTPUTS_PATH="$(cd "$(dirname "$OUTPUTS_PATH")" 2>/dev/null && pwd)/$(basename "$OUTPUTS_PATH")"
|
||||
fi
|
||||
|
||||
: "${START_PERCENTILE:=0}" # Default to 0 percentile (first line)
|
||||
: "${END_PERCENTILE:=100}" # Default to 100 percentile (last line)
|
||||
|
||||
# Validate percentile ranges if provided
|
||||
if ! [[ "$START_PERCENTILE" =~ ^[0-9]+$ ]] || ! [[ "$END_PERCENTILE" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: Percentiles must be integers"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$START_PERCENTILE" -ge "$END_PERCENTILE" ]; then
|
||||
echo "Error: Start percentile must be less than end percentile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$START_PERCENTILE" -lt 0 ] || [ "$END_PERCENTILE" -gt 100 ]; then
|
||||
echo "Error: Percentiles must be between 0 and 100"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using agent LLM config: $AGENT_LLM_CONFIG"
|
||||
echo "Using environment LLM config: $ENV_LLM_CONFIG"
|
||||
echo "Outputs path: $OUTPUTS_PATH"
|
||||
echo "Server hostname: $SERVER_HOSTNAME"
|
||||
echo "Version: $VERSION"
|
||||
echo "Start Percentile: $START_PERCENTILE"
|
||||
echo "End Percentile: $END_PERCENTILE"
|
||||
|
||||
echo "Downloading tasks.md..."
|
||||
rm -f tasks.md
|
||||
wget https://github.com/TheAgentCompany/TheAgentCompany/releases/download/${VERSION}/tasks.md
|
||||
|
||||
total_lines=$(cat tasks.md | grep "ghcr.io/theagentcompany" | wc -l)
|
||||
if [ "$total_lines" -ne 175 ]; then
|
||||
echo "Error: Expected 175 tasks in tasks.md but found $total_lines lines"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Calculate line numbers based on percentiles
|
||||
start_line=$(echo "scale=0; ($total_lines * $START_PERCENTILE / 100) + 1" | bc)
|
||||
end_line=$(echo "scale=0; $total_lines * $END_PERCENTILE / 100" | bc)
|
||||
|
||||
echo "Using tasks No. $start_line to $end_line (inclusive) out of 1-175 tasks"
|
||||
|
||||
# Create a temporary file with just the desired range
|
||||
temp_file="tasks_${START_PERCENTILE}_${END_PERCENTILE}.md"
|
||||
sed -n "${start_line},${end_line}p" tasks.md > "$temp_file"
|
||||
|
||||
while IFS= read -r task_image; do
|
||||
docker pull $task_image
|
||||
|
||||
@@ -153,8 +108,8 @@ while IFS= read -r task_image; do
|
||||
docker images "ghcr.io/all-hands-ai/runtime" -q | xargs -r docker rmi -f
|
||||
docker volume prune -f
|
||||
docker system prune -f
|
||||
done < "$temp_file"
|
||||
done < tasks.md
|
||||
|
||||
rm tasks.md "$temp_file"
|
||||
rm tasks.md
|
||||
|
||||
echo "All evaluation completed successfully!"
|
||||
|
||||
@@ -50,7 +50,6 @@ def get_config(
|
||||
base_container_image='python:3.12-bookworm',
|
||||
enable_auto_lint=True,
|
||||
use_host_network=False,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -79,7 +79,6 @@ def get_config(
|
||||
'VWA_HOMEPAGE': f'{base_url}:4399',
|
||||
},
|
||||
timeout=300,
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -71,7 +71,6 @@ def get_config(
|
||||
'MAP': f'{base_url}:3000',
|
||||
'HOMEPAGE': f'{base_url}:4399',
|
||||
},
|
||||
remote_runtime_enable_retries=True,
|
||||
),
|
||||
# do not mount workspace
|
||||
workspace_base=None,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, it, expect, test } from "vitest";
|
||||
import { ChatMessage } from "#/components/features/chat/chat-message";
|
||||
|
||||
describe("ChatMessage", () => {
|
||||
@@ -45,9 +45,7 @@ describe("ChatMessage", () => {
|
||||
|
||||
await user.click(copyToClipboardButton);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(navigator.clipboard.readText()).resolves.toBe("Hello, World!"),
|
||||
);
|
||||
expect(navigator.clipboard.readText()).resolves.toBe("Hello, World!");
|
||||
});
|
||||
|
||||
it("should display an error toast if copying content to clipboard fails", async () => {});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { screen } from "@testing-library/react";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import { ExpandableMessage } from "#/components/features/chat/expandable-message";
|
||||
import { vi } from "vitest"
|
||||
import { vi } from "vitest";
|
||||
|
||||
vi.mock("react-i18next", async () => {
|
||||
const actual = await vi.importActual("react-i18next");
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { SettingsProvider } from "#/context/settings-context";
|
||||
import { AuthProvider } from "#/context/auth-context";
|
||||
|
||||
describe("AnalyticsConsentFormModal", () => {
|
||||
it("should call saveUserSettings with default settings on confirm reset settings", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onCloseMock = vi.fn();
|
||||
const saveUserSettingsSpy = vi.spyOn(OpenHands, "saveSettings");
|
||||
|
||||
render(<AnalyticsConsentFormModal onClose={onCloseMock} />, {
|
||||
wrapper: ({ children }) => (
|
||||
<AuthProvider>
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
<SettingsProvider>{children}</SettingsProvider>
|
||||
</QueryClientProvider>
|
||||
</AuthProvider>
|
||||
),
|
||||
});
|
||||
|
||||
const confirmButton = screen.getByTestId("confirm-preferences");
|
||||
await user.click(confirmButton);
|
||||
|
||||
expect(saveUserSettingsSpy).toHaveBeenCalledWith({
|
||||
user_consents_to_analytics: true,
|
||||
agent: "CodeActAgent",
|
||||
confirmation_mode: false,
|
||||
enable_default_condenser: false,
|
||||
github_token: undefined,
|
||||
language: "en",
|
||||
llm_api_key: undefined,
|
||||
llm_base_url: "",
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
unset_github_token: undefined,
|
||||
});
|
||||
expect(onCloseMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
+8
-43
@@ -1,14 +1,5 @@
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import {
|
||||
afterAll,
|
||||
afterEach,
|
||||
beforeAll,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
test,
|
||||
vi,
|
||||
} from "vitest";
|
||||
import { afterEach, describe, expect, it, test, vi } from "vitest";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { formatTimeDelta } from "#/utils/format-time-delta";
|
||||
import { ConversationCard } from "#/components/features/conversation-panel/conversation-card";
|
||||
@@ -20,18 +11,10 @@ describe("ConversationCard", () => {
|
||||
const onChangeTitle = vi.fn();
|
||||
const onDownloadWorkspace = vi.fn();
|
||||
|
||||
beforeAll(() => {
|
||||
vi.stubGlobal("window", { open: vi.fn() });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("should render the conversation card", () => {
|
||||
render(
|
||||
<ConversationCard
|
||||
@@ -46,8 +29,9 @@ describe("ConversationCard", () => {
|
||||
const expectedDate = `${formatTimeDelta(new Date("2021-10-01T12:00:00Z"))} ago`;
|
||||
|
||||
const card = screen.getByTestId("conversation-card");
|
||||
const title = within(card).getByTestId("conversation-card-title");
|
||||
|
||||
within(card).getByText("Conversation 1");
|
||||
expect(title).toHaveValue("Conversation 1");
|
||||
within(card).getByText(expectedDate);
|
||||
});
|
||||
|
||||
@@ -164,8 +148,10 @@ describe("ConversationCard", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
await clickOnEditButton(user);
|
||||
const title = screen.getByTestId("conversation-card-title");
|
||||
expect(title).toBeDisabled();
|
||||
|
||||
await clickOnEditButton(user);
|
||||
|
||||
expect(title).toBeEnabled();
|
||||
expect(screen.queryByTestId("context-menu")).not.toBeInTheDocument();
|
||||
@@ -178,6 +164,7 @@ describe("ConversationCard", () => {
|
||||
|
||||
expect(onChangeTitle).toHaveBeenCalledWith("New Conversation Name");
|
||||
expect(title).toHaveValue("New Conversation Name");
|
||||
expect(title).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should reset title and not call onChangeTitle when the title is empty", async () => {
|
||||
@@ -204,27 +191,7 @@ describe("ConversationCard", () => {
|
||||
expect(title).toHaveValue("Conversation 1");
|
||||
});
|
||||
|
||||
test("clicking the title should trigger the onClick handler", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<ConversationCard
|
||||
onClick={onClick}
|
||||
onDelete={onDelete}
|
||||
isActive
|
||||
onChangeTitle={onChangeTitle}
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
/>,
|
||||
);
|
||||
|
||||
const title = screen.getByTestId("conversation-card-title");
|
||||
await user.click(title);
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("clicking the title should not trigger the onClick handler if edit mode", async () => {
|
||||
test("clicking the title should not trigger the onClick handler", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<ConversationCard
|
||||
@@ -237,8 +204,6 @@ describe("ConversationCard", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
await clickOnEditButton(user);
|
||||
|
||||
const title = screen.getByTestId("conversation-card-title");
|
||||
await user.click(title);
|
||||
|
||||
|
||||
+4
-8
@@ -179,10 +179,9 @@ describe("ConversationPanel", () => {
|
||||
const user = userEvent.setup();
|
||||
renderConversationPanel();
|
||||
const cards = await screen.findAllByTestId("conversation-card");
|
||||
const title = within(cards[0]).getByTestId("conversation-card-title");
|
||||
|
||||
const card = cards[0];
|
||||
await clickOnEditButton(user, card);
|
||||
const title = within(card).getByTestId("conversation-card-title");
|
||||
await clickOnEditButton(user);
|
||||
|
||||
await user.clear(title);
|
||||
await user.type(title, "Conversation 1 Renamed");
|
||||
@@ -203,10 +202,7 @@ describe("ConversationPanel", () => {
|
||||
const user = userEvent.setup();
|
||||
renderConversationPanel();
|
||||
const cards = await screen.findAllByTestId("conversation-card");
|
||||
|
||||
const card = cards[0];
|
||||
await clickOnEditButton(user, card);
|
||||
const title = within(card).getByTestId("conversation-card-title");
|
||||
const title = within(cards[0]).getByTestId("conversation-card-title");
|
||||
|
||||
await user.click(title);
|
||||
await user.tab();
|
||||
@@ -231,7 +227,7 @@ describe("ConversationPanel", () => {
|
||||
it("should call onClose after clicking a card", async () => {
|
||||
renderConversationPanel();
|
||||
const cards = await screen.findAllByTestId("conversation-card");
|
||||
const firstCard = cards[1];
|
||||
const firstCard = cards[0];
|
||||
|
||||
await userEvent.click(firstCard);
|
||||
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import { screen, within } from "@testing-library/react";
|
||||
import { UserEvent } from "@testing-library/user-event";
|
||||
|
||||
export const clickOnEditButton = async (
|
||||
user: UserEvent,
|
||||
container?: HTMLElement,
|
||||
) => {
|
||||
const wrapper = container ? within(container) : screen;
|
||||
|
||||
const ellipsisButton = wrapper.getByTestId("ellipsis-button");
|
||||
export const clickOnEditButton = async (user: UserEvent) => {
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
const menu = wrapper.getByTestId("context-menu");
|
||||
const menu = screen.getByTestId("context-menu");
|
||||
const editButton = within(menu).getByTestId("edit-button");
|
||||
|
||||
await user.click(editButton);
|
||||
|
||||
@@ -3,7 +3,6 @@ import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import { createRoutesStub } from "react-router";
|
||||
import { AxiosError } from "axios";
|
||||
import { Sidebar } from "#/components/features/sidebar/sidebar";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
@@ -83,10 +82,6 @@ describe("Sidebar", () => {
|
||||
within(accountSettingsModal).getByLabelText(/GITHUB\$TOKEN_LABEL/i);
|
||||
await user.type(tokenInput, "new-token");
|
||||
|
||||
const analyticsConsentInput =
|
||||
within(accountSettingsModal).getByTestId("analytics-consent");
|
||||
await user.click(analyticsConsentInput);
|
||||
|
||||
const saveButton =
|
||||
within(accountSettingsModal).getByTestId("save-settings");
|
||||
await user.click(saveButton);
|
||||
@@ -101,7 +96,6 @@ describe("Sidebar", () => {
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -154,24 +148,11 @@ describe("Sidebar", () => {
|
||||
expect(settingsModal).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should open the settings modal if GET /settings fails with a 404", async () => {
|
||||
const error = new AxiosError(
|
||||
"Request failed with status code 404",
|
||||
"ERR_BAD_REQUEST",
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
data: { message: "Settings not found" },
|
||||
headers: {},
|
||||
// @ts-expect-error - we only need the response object for this test
|
||||
config: {},
|
||||
},
|
||||
it("should open the settings modal if GET /settings fails", async () => {
|
||||
vi.spyOn(OpenHands, "getSettings").mockRejectedValue(
|
||||
new Error("Failed to fetch settings"),
|
||||
);
|
||||
|
||||
vi.spyOn(OpenHands, "getSettings").mockRejectedValue(error);
|
||||
|
||||
renderSidebar();
|
||||
|
||||
const settingsModal = await screen.findByTestId("ai-config-modal");
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("FeedbackForm", () => {
|
||||
screen.getByLabelText(I18nKey.FEEDBACK$PRIVATE_LABEL);
|
||||
screen.getByLabelText(I18nKey.FEEDBACK$PUBLIC_LABEL);
|
||||
|
||||
screen.getByRole("button", { name: I18nKey.FEEDBACK$SHARE_LABEL });
|
||||
screen.getByRole("button", { name: I18nKey.FEEDBACK$CONTRIBUTE_LABEL });
|
||||
screen.getByRole("button", { name: I18nKey.FEEDBACK$CANCEL_LABEL });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,74 +1,16 @@
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import { AccountSettingsModal } from "#/components/shared/modals/account-settings/account-settings-modal";
|
||||
import { MOCK_DEFAULT_USER_SETTINGS } from "#/mocks/handlers";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import * as ConsentHandlers from "#/utils/handle-capture-consent";
|
||||
|
||||
describe("AccountSettingsModal", () => {
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
const saveSettingsSpy = vi.spyOn(OpenHands, "saveSettings");
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it.skip("should set the appropriate user analytics consent default", async () => {
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
user_consents_to_analytics: true,
|
||||
});
|
||||
renderWithProviders(<AccountSettingsModal onClose={() => {}} />);
|
||||
|
||||
const analyticsConsentInput = screen.getByTestId("analytics-consent");
|
||||
await waitFor(() => expect(analyticsConsentInput).toBeChecked());
|
||||
});
|
||||
|
||||
it("should save the users consent to analytics when saving account settings", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<AccountSettingsModal onClose={() => {}} />);
|
||||
|
||||
const analyticsConsentInput = screen.getByTestId("analytics-consent");
|
||||
await user.click(analyticsConsentInput);
|
||||
|
||||
const saveButton = screen.getByTestId("save-settings");
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(saveSettingsSpy).toHaveBeenCalledWith({
|
||||
agent: "CodeActAgent",
|
||||
confirmation_mode: false,
|
||||
enable_default_condenser: false,
|
||||
language: "en",
|
||||
llm_base_url: "",
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("should call handleCaptureConsent with the analytics consent value if the save is successful", async () => {
|
||||
const user = userEvent.setup();
|
||||
const handleCaptureConsentSpy = vi.spyOn(
|
||||
ConsentHandlers,
|
||||
"handleCaptureConsent",
|
||||
);
|
||||
renderWithProviders(<AccountSettingsModal onClose={() => {}} />);
|
||||
|
||||
const analyticsConsentInput = screen.getByTestId("analytics-consent");
|
||||
await user.click(analyticsConsentInput);
|
||||
|
||||
const saveButton = screen.getByTestId("save-settings");
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(true);
|
||||
|
||||
await user.click(analyticsConsentInput);
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(false);
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should send all settings data when saving account settings", async () => {
|
||||
@@ -97,11 +39,11 @@ describe("AccountSettingsModal", () => {
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("should render a checkmark and not the input if the github token is set", async () => {
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
github_token_is_set: true,
|
||||
@@ -119,6 +61,7 @@ describe("AccountSettingsModal", () => {
|
||||
|
||||
it("should send an unset github token property when pressing disconnect", async () => {
|
||||
const user = userEvent.setup();
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
github_token_is_set: true,
|
||||
@@ -143,6 +86,7 @@ describe("AccountSettingsModal", () => {
|
||||
|
||||
it("should not unset the github token when changing the language", async () => {
|
||||
const user = userEvent.setup();
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
github_token_is_set: true,
|
||||
@@ -167,7 +111,6 @@ describe("AccountSettingsModal", () => {
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,10 +8,9 @@ vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
SUGGESTIONS$TODO_APP: "ToDoリストアプリを開発する",
|
||||
LANDING$BUILD_APP_BUTTON: "プルリクエストを表示するアプリを開発する",
|
||||
SUGGESTIONS$HACKER_NEWS:
|
||||
"Hacker Newsのトップ記事を表示するbashスクリプトを作成する",
|
||||
"SUGGESTIONS$TODO_APP": "ToDoリストアプリを開発する",
|
||||
"LANDING$BUILD_APP_BUTTON": "プルリクエストを表示するアプリを開発する",
|
||||
"SUGGESTIONS$HACKER_NEWS": "Hacker Newsのトップ記事を表示するbashスクリプトを作成する",
|
||||
};
|
||||
return translations[key] || key;
|
||||
},
|
||||
@@ -39,9 +38,9 @@ describe("SuggestionItem", () => {
|
||||
value: "todo app value",
|
||||
};
|
||||
|
||||
render(
|
||||
<SuggestionItem suggestion={translatedSuggestion} onClick={onClick} />,
|
||||
);
|
||||
const { container } = render(<SuggestionItem suggestion={translatedSuggestion} onClick={onClick} />);
|
||||
console.log('Rendered HTML:', container.innerHTML);
|
||||
|
||||
|
||||
expect(screen.getByText("ToDoリストアプリを開発する")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -58,7 +58,6 @@ describe("frontend/routes/_oh", () => {
|
||||
it("should render and capture the user's consent if oss mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
const handleCaptureConsentSpy = vi.spyOn(
|
||||
CaptureConsent,
|
||||
"handleCaptureConsent",
|
||||
@@ -70,16 +69,12 @@ describe("frontend/routes/_oh", () => {
|
||||
POSTHOG_CLIENT_KEY: "test-key",
|
||||
});
|
||||
|
||||
// @ts-expect-error - We only care about the user_consents_to_analytics field
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
user_consents_to_analytics: null,
|
||||
});
|
||||
|
||||
renderWithProviders(<RouteStub />);
|
||||
|
||||
// The user has not consented to tracking
|
||||
const consentForm = await screen.findByTestId("user-capture-consent-form");
|
||||
expect(handleCaptureConsentSpy).not.toHaveBeenCalled();
|
||||
expect(localStorage.getItem("analytics-consent")).toBeNull();
|
||||
|
||||
const submitButton = within(consentForm).getByRole("button", {
|
||||
name: /confirm preferences/i,
|
||||
@@ -88,6 +83,7 @@ describe("frontend/routes/_oh", () => {
|
||||
|
||||
// The user has now consented to tracking
|
||||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(true);
|
||||
expect(localStorage.getItem("analytics-consent")).toBe("true");
|
||||
expect(
|
||||
screen.queryByTestId("user-capture-consent-form"),
|
||||
).not.toBeInTheDocument();
|
||||
@@ -110,6 +106,17 @@ describe("frontend/routes/_oh", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the user consent form if the user has already made a decision", async () => {
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
renderWithProviders(<RouteStub />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByTestId("user-capture-consent-form"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Likely failing due to how tokens are now handled in context. Move to e2e tests
|
||||
it.skip("should render a new project button if a token is set", async () => {
|
||||
localStorage.setItem("token", "test-token");
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.23.0",
|
||||
"version": "0.21.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.23.0",
|
||||
"version": "0.21.0",
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@nextui-org/react": "^2.6.11",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.23.0",
|
||||
"version": "0.21.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const retrieveGitHubAppRepositories = async (
|
||||
per_page = 30,
|
||||
) => {
|
||||
const installationId = installations[installationIndex];
|
||||
const response = await openHands.get<GitHubRepository[]>(
|
||||
const response = await openHands.get<GitHubAppRepository>(
|
||||
"/api/github/repositories",
|
||||
{
|
||||
params: {
|
||||
@@ -26,11 +26,7 @@ export const retrieveGitHubAppRepositories = async (
|
||||
},
|
||||
);
|
||||
|
||||
const link =
|
||||
response.data.length > 0 && response.data[0].link_header
|
||||
? response.data[0].link_header
|
||||
: "";
|
||||
|
||||
const link = response.headers.link ?? "";
|
||||
const nextPage = extractNextPageFromLink(link);
|
||||
let nextInstallation: number | null;
|
||||
|
||||
@@ -43,7 +39,7 @@ export const retrieveGitHubAppRepositories = async (
|
||||
}
|
||||
|
||||
return {
|
||||
data: response.data,
|
||||
data: response.data.repositories,
|
||||
nextPage,
|
||||
installationIndex: nextInstallation,
|
||||
};
|
||||
@@ -68,10 +64,7 @@ export const retrieveGitHubUserRepositories = async (
|
||||
},
|
||||
);
|
||||
|
||||
const link =
|
||||
response.data.length > 0 && response.data[0].link_header
|
||||
? response.data[0].link_header
|
||||
: "";
|
||||
const link = response.headers.link ?? "";
|
||||
const nextPage = extractNextPageFromLink(link);
|
||||
|
||||
return { data: response.data, nextPage };
|
||||
|
||||
@@ -254,6 +254,35 @@ class OpenHands {
|
||||
return data;
|
||||
}
|
||||
|
||||
static async searchEvents(
|
||||
conversationId: string,
|
||||
params: {
|
||||
query?: string;
|
||||
startId?: number;
|
||||
limit?: number;
|
||||
eventType?: string;
|
||||
source?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
},
|
||||
): Promise<{ events: Record<string, unknown>[]; has_more: boolean }> {
|
||||
const { data } = await openHands.get<{
|
||||
events: Record<string, unknown>[];
|
||||
has_more: boolean;
|
||||
}>(`/api/conversations/${conversationId}/events/search`, {
|
||||
params: {
|
||||
query: params.query,
|
||||
start_id: params.startId,
|
||||
limit: params.limit,
|
||||
event_type: params.eventType,
|
||||
source: params.source,
|
||||
start_date: params.startDate,
|
||||
end_date: params.endDate,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings from the server or use the default settings if not found
|
||||
*/
|
||||
@@ -297,7 +326,7 @@ class OpenHands {
|
||||
query: string,
|
||||
per_page = 5,
|
||||
): Promise<GitHubRepository[]> {
|
||||
const response = await openHands.get<GitHubRepository[]>(
|
||||
const response = await openHands.get<{ items: GitHubRepository[] }>(
|
||||
"/api/github/search/repositories",
|
||||
{
|
||||
params: {
|
||||
@@ -307,7 +336,7 @@ class OpenHands {
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
return response.data.items;
|
||||
}
|
||||
|
||||
static async getTrajectory(
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
} from "#/components/shared/modals/confirmation-modals/base-modal";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { useCurrentSettings } from "#/context/settings-context";
|
||||
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
|
||||
|
||||
interface AnalyticsConsentFormModalProps {
|
||||
@@ -15,21 +14,13 @@ interface AnalyticsConsentFormModalProps {
|
||||
export function AnalyticsConsentFormModal({
|
||||
onClose,
|
||||
}: AnalyticsConsentFormModalProps) {
|
||||
const { saveUserSettings } = useCurrentSettings();
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const analytics = formData.get("analytics") === "on";
|
||||
|
||||
await saveUserSettings(
|
||||
{ user_consents_to_analytics: analytics },
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleCaptureConsent(analytics);
|
||||
},
|
||||
},
|
||||
);
|
||||
handleCaptureConsent(analytics);
|
||||
localStorage.setItem("analytics-consent", analytics.toString());
|
||||
|
||||
onClose();
|
||||
};
|
||||
@@ -55,7 +46,6 @@ export function AnalyticsConsentFormModal({
|
||||
</label>
|
||||
|
||||
<ModalButton
|
||||
testId="confirm-preferences"
|
||||
type="submit"
|
||||
text="Confirm Preferences"
|
||||
className="bg-primary text-white w-full hover:opacity-80"
|
||||
|
||||
@@ -180,7 +180,8 @@ export function ChatInterface() {
|
||||
onStop={handleStop}
|
||||
isDisabled={
|
||||
curAgentState === AgentState.LOADING ||
|
||||
curAgentState === AgentState.AWAITING_USER_CONFIRMATION
|
||||
curAgentState === AgentState.AWAITING_USER_CONFIRMATION ||
|
||||
curAgentState === AgentState.RATE_LIMITED
|
||||
}
|
||||
mode={curAgentState === AgentState.RUNNING ? "stop" : "submit"}
|
||||
value={messageToSend ?? undefined}
|
||||
|
||||
@@ -58,10 +58,8 @@ export function ConversationCard({
|
||||
};
|
||||
|
||||
const handleInputClick = (event: React.MouseEvent<HTMLInputElement>) => {
|
||||
if (titleMode === "edit") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
const handleDelete = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
@@ -103,26 +101,17 @@ export function ConversationCard({
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
{isActive && <span className="w-2 h-2 bg-blue-500 rounded-full" />}
|
||||
{titleMode === "edit" && (
|
||||
<input
|
||||
ref={inputRef}
|
||||
data-testid="conversation-card-title"
|
||||
onClick={handleInputClick}
|
||||
onBlur={handleBlur}
|
||||
onKeyUp={handleKeyUp}
|
||||
type="text"
|
||||
defaultValue={title}
|
||||
className="text-sm leading-6 font-semibold bg-transparent w-full"
|
||||
/>
|
||||
)}
|
||||
{titleMode === "view" && (
|
||||
<p
|
||||
data-testid="conversation-card-title"
|
||||
className="text-sm leading-6 font-semibold bg-transparent w-full"
|
||||
>
|
||||
{title}
|
||||
</p>
|
||||
)}
|
||||
<input
|
||||
ref={inputRef}
|
||||
disabled={titleMode === "view"}
|
||||
data-testid="conversation-card-title"
|
||||
onClick={handleInputClick}
|
||||
onBlur={handleBlur}
|
||||
onKeyUp={handleKeyUp}
|
||||
type="text"
|
||||
defaultValue={title}
|
||||
className="text-sm leading-6 font-semibold bg-transparent w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 relative">
|
||||
@@ -154,7 +143,10 @@ export function ConversationCard({
|
||||
)}
|
||||
>
|
||||
{selectedRepository && (
|
||||
<ConversationRepoLink selectedRepository={selectedRepository} />
|
||||
<ConversationRepoLink
|
||||
selectedRepository={selectedRepository}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
<p className="text-xs text-neutral-400">
|
||||
<time>{formatTimeDelta(new Date(lastUpdatedAt))} ago</time>
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
interface ConversationRepoLinkProps {
|
||||
selectedRepository: string;
|
||||
onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void;
|
||||
}
|
||||
|
||||
export function ConversationRepoLink({
|
||||
selectedRepository,
|
||||
onClick,
|
||||
}: ConversationRepoLinkProps) {
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
window.open(
|
||||
`https://github.com/${selectedRepository}`,
|
||||
"_blank",
|
||||
"noopener,noreferrer",
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
<a
|
||||
data-testid="conversation-card-selected-repository"
|
||||
onClick={handleClick}
|
||||
href={`https://github.com/${selectedRepository}`}
|
||||
target="_blank noopener noreferrer"
|
||||
onClick={onClick}
|
||||
className="text-xs text-neutral-400 hover:text-neutral-200"
|
||||
>
|
||||
{selectedRepository}
|
||||
</button>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export function FeedbackForm({ onClose, polarity }: FeedbackFormProps) {
|
||||
<ModalButton
|
||||
disabled={isPending}
|
||||
type="submit"
|
||||
text={t(I18nKey.FEEDBACK$SHARE_LABEL)}
|
||||
text={t(I18nKey.FEEDBACK$CONTRIBUTE_LABEL)}
|
||||
className="bg-[#4465DB] grow"
|
||||
/>
|
||||
<ModalButton
|
||||
|
||||
@@ -70,7 +70,7 @@ export function GitHubRepositorySelector({
|
||||
}}
|
||||
onSelectionChange={(id) => handleRepoSelection(id?.toString() ?? null)}
|
||||
onInputChange={onInputChange}
|
||||
clearButtonProps={{ onPress: handleClearSelection }}
|
||||
clearButtonProps={{ onClick: handleClearSelection }}
|
||||
listboxProps={{
|
||||
emptyContent,
|
||||
}}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from "react";
|
||||
import { FaListUl } from "react-icons/fa";
|
||||
import { useDispatch } from "react-redux";
|
||||
import posthog from "posthog-js";
|
||||
import toast from "react-hot-toast";
|
||||
import { useGitHubUser } from "#/hooks/query/use-github-user";
|
||||
import { UserActions } from "./user-actions";
|
||||
import { AllHandsLogoButton } from "#/components/shared/buttons/all-hands-logo-button";
|
||||
@@ -29,12 +28,7 @@ export function Sidebar() {
|
||||
const endSession = useEndSession();
|
||||
const user = useGitHubUser();
|
||||
const { data: config } = useConfig();
|
||||
const {
|
||||
data: settings,
|
||||
error: settingsError,
|
||||
isError: settingsIsError,
|
||||
isFetching: isFetchingSettings,
|
||||
} = useSettings();
|
||||
const { data: settings, isError: settingsError } = useSettings();
|
||||
const { mutateAsync: logout } = useLogout();
|
||||
const { saveUserSettings } = useCurrentSettings();
|
||||
|
||||
@@ -52,20 +46,6 @@ export function Sidebar() {
|
||||
}
|
||||
}, [user.isError]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// We don't show toast errors for settings in the global error handler
|
||||
// because we have a special case for 404 errors
|
||||
if (
|
||||
!isFetchingSettings &&
|
||||
settingsIsError &&
|
||||
settingsError?.status !== 404
|
||||
) {
|
||||
toast.error(
|
||||
"Something went wrong while fetching settings. Please reload the page.",
|
||||
);
|
||||
}
|
||||
}, [settingsError?.status, settingsError, isFetchingSettings]);
|
||||
|
||||
const handleEndSession = () => {
|
||||
dispatch(setCurrentAgentState(AgentState.LOADING));
|
||||
endSession();
|
||||
@@ -125,7 +105,7 @@ export function Sidebar() {
|
||||
{accountSettingsModalOpen && (
|
||||
<AccountSettingsModal onClose={handleAccountSettingsModalClose} />
|
||||
)}
|
||||
{(settingsError?.status === 404 || settingsModalIsOpen) && (
|
||||
{(settingsError || settingsModalIsOpen) && (
|
||||
<SettingsModal
|
||||
settings={settings}
|
||||
onClose={() => setSettingsModalIsOpen(false)}
|
||||
|
||||
@@ -15,21 +15,25 @@ import { useConfig } from "#/hooks/query/use-config";
|
||||
import { useCurrentSettings } from "#/context/settings-context";
|
||||
import { GitHubTokenInput } from "./github-token-input";
|
||||
import { PostSettings } from "#/types/settings";
|
||||
import { useGitHubUser } from "#/hooks/query/use-github-user";
|
||||
|
||||
interface AccountSettingsFormProps {
|
||||
onClose: () => void;
|
||||
selectedLanguage: string;
|
||||
gitHubError: boolean;
|
||||
analyticsConsent: string | null;
|
||||
}
|
||||
|
||||
export function AccountSettingsForm({ onClose }: AccountSettingsFormProps) {
|
||||
const { isError: isGitHubError } = useGitHubUser();
|
||||
export function AccountSettingsForm({
|
||||
onClose,
|
||||
selectedLanguage,
|
||||
gitHubError,
|
||||
analyticsConsent,
|
||||
}: AccountSettingsFormProps) {
|
||||
const { data: config } = useConfig();
|
||||
const { saveUserSettings, settings } = useCurrentSettings();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const githubTokenIsSet = !!settings?.GITHUB_TOKEN_IS_SET;
|
||||
const analyticsConsentValue = !!settings?.USER_CONSENTS_TO_ANALYTICS;
|
||||
const selectedLanguage = settings?.LANGUAGE || "en";
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -40,7 +44,6 @@ export function AccountSettingsForm({ onClose }: AccountSettingsFormProps) {
|
||||
const analytics = formData.get("analytics")?.toString() === "on";
|
||||
|
||||
const newSettings: Partial<PostSettings> = {};
|
||||
newSettings.user_consents_to_analytics = analytics;
|
||||
|
||||
if (ghToken) newSettings.github_token = ghToken;
|
||||
|
||||
@@ -54,11 +57,11 @@ export function AccountSettingsForm({ onClose }: AccountSettingsFormProps) {
|
||||
if (languageKey) newSettings.LANGUAGE = languageKey;
|
||||
}
|
||||
|
||||
await saveUserSettings(newSettings, {
|
||||
onSuccess: () => {
|
||||
handleCaptureConsent(analytics);
|
||||
},
|
||||
});
|
||||
await saveUserSettings(newSettings);
|
||||
|
||||
handleCaptureConsent(analytics);
|
||||
const ANALYTICS = analytics.toString();
|
||||
localStorage.setItem("analytics-consent", ANALYTICS);
|
||||
|
||||
onClose();
|
||||
};
|
||||
@@ -114,12 +117,12 @@ export function AccountSettingsForm({ onClose }: AccountSettingsFormProps) {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isGitHubError && (
|
||||
{gitHubError && (
|
||||
<p className="text-danger text-xs">
|
||||
{t(I18nKey.GITHUB$TOKEN_INVALID)}
|
||||
</p>
|
||||
)}
|
||||
{githubTokenIsSet && !isGitHubError && (
|
||||
{githubTokenIsSet && !gitHubError && (
|
||||
<ModalButton
|
||||
testId="disconnect-github"
|
||||
variant="text-like"
|
||||
@@ -132,10 +135,9 @@ export function AccountSettingsForm({ onClose }: AccountSettingsFormProps) {
|
||||
|
||||
<label className="flex gap-2 items-center self-start">
|
||||
<input
|
||||
data-testid="analytics-consent"
|
||||
name="analytics"
|
||||
type="checkbox"
|
||||
defaultChecked={analyticsConsentValue}
|
||||
defaultChecked={analyticsConsent === "true"}
|
||||
/>
|
||||
{t(I18nKey.ANALYTICS$ENABLE)}
|
||||
</label>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useGitHubUser } from "#/hooks/query/use-github-user";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { ModalBackdrop } from "../modal-backdrop";
|
||||
import { AccountSettingsForm } from "./account-settings-form";
|
||||
|
||||
@@ -6,9 +8,20 @@ interface AccountSettingsModalProps {
|
||||
}
|
||||
|
||||
export function AccountSettingsModal({ onClose }: AccountSettingsModalProps) {
|
||||
const user = useGitHubUser();
|
||||
const { data: settings } = useSettings();
|
||||
|
||||
// FIXME: Bad practice to use localStorage directly
|
||||
const analyticsConsent = localStorage.getItem("analytics-consent");
|
||||
|
||||
return (
|
||||
<ModalBackdrop onClose={onClose}>
|
||||
<AccountSettingsForm onClose={onClose} />
|
||||
<AccountSettingsForm
|
||||
onClose={onClose}
|
||||
selectedLanguage={settings?.LANGUAGE || "en"}
|
||||
gitHubError={user.isError}
|
||||
analyticsConsent={analyticsConsent}
|
||||
/>
|
||||
</ModalBackdrop>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import React from "react";
|
||||
import { MutateOptions } from "@tanstack/react-query";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { useSaveSettings } from "#/hooks/mutation/use-save-settings";
|
||||
import { PostSettings, Settings } from "#/types/settings";
|
||||
|
||||
type SaveUserSettingsConfig = {
|
||||
onSuccess: MutateOptions<void, Error, Partial<PostSettings>>["onSuccess"];
|
||||
};
|
||||
|
||||
interface SettingsContextType {
|
||||
saveUserSettings: (
|
||||
newSettings: Partial<PostSettings>,
|
||||
config?: SaveUserSettingsConfig,
|
||||
) => Promise<void>;
|
||||
saveUserSettings: (newSettings: Partial<PostSettings>) => Promise<void>;
|
||||
settings: Settings | undefined;
|
||||
}
|
||||
|
||||
@@ -28,10 +20,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||
const { data: userSettings } = useSettings();
|
||||
const { mutateAsync: saveSettings } = useSaveSettings();
|
||||
|
||||
const saveUserSettings = async (
|
||||
newSettings: Partial<PostSettings>,
|
||||
config?: SaveUserSettingsConfig,
|
||||
) => {
|
||||
const saveUserSettings = async (newSettings: Partial<PostSettings>) => {
|
||||
const updatedSettings: Partial<PostSettings> = {
|
||||
...userSettings,
|
||||
...newSettings,
|
||||
@@ -41,7 +30,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||
delete updatedSettings.LLM_API_KEY;
|
||||
}
|
||||
|
||||
await saveSettings(updatedSettings, { onSuccess: config?.onSuccess });
|
||||
await saveSettings(updatedSettings);
|
||||
};
|
||||
|
||||
const value = React.useMemo(
|
||||
|
||||
@@ -16,7 +16,6 @@ const saveSettingsMutationFn = async (settings: Partial<PostSettings>) => {
|
||||
github_token: settings.github_token,
|
||||
unset_github_token: settings.unset_github_token,
|
||||
enable_default_condenser: settings.ENABLE_DEFAULT_CONDENSER,
|
||||
user_consents_to_analytics: settings.user_consents_to_analytics,
|
||||
};
|
||||
|
||||
await OpenHands.saveSettings(apiSettings);
|
||||
|
||||
@@ -23,9 +23,6 @@ export const useActiveHost = () => {
|
||||
},
|
||||
enabled: !RUNTIME_INACTIVE_STATES.includes(curAgentState),
|
||||
initialData: { hosts: [] },
|
||||
meta: {
|
||||
disableToast: true,
|
||||
},
|
||||
});
|
||||
|
||||
const apps = useQueries({
|
||||
@@ -40,9 +37,6 @@ export const useActiveHost = () => {
|
||||
}
|
||||
},
|
||||
refetchInterval: 3000,
|
||||
meta: {
|
||||
disableToast: true,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
|
||||
@@ -16,8 +16,5 @@ export const useIsAuthed = () => {
|
||||
enabled: !!appMode,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
retry: false,
|
||||
meta: {
|
||||
disableToast: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useConversation } from "#/context/conversation-context";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
export const useSearchEvents = (params: {
|
||||
query?: string;
|
||||
startId?: number;
|
||||
limit?: number;
|
||||
eventType?: string;
|
||||
source?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}) => {
|
||||
const { conversationId } = useConversation();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["search_events", conversationId, params],
|
||||
queryFn: () => {
|
||||
if (!conversationId) throw new Error("No conversation ID");
|
||||
return OpenHands.searchEvents(conversationId, params);
|
||||
},
|
||||
enabled: !!conversationId,
|
||||
});
|
||||
};
|
||||
@@ -19,7 +19,6 @@ const getSettingsQueryFn = async () => {
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: apiSettings.remote_runtime_resource_factor,
|
||||
GITHUB_TOKEN_IS_SET: apiSettings.github_token_is_set,
|
||||
ENABLE_DEFAULT_CONDENSER: apiSettings.enable_default_condenser,
|
||||
USER_CONSENTS_TO_ANALYTICS: apiSettings.user_consents_to_analytics,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,9 +31,6 @@ export const useSettings = () => {
|
||||
initialData: DEFAULT_SETTINGS,
|
||||
staleTime: 0,
|
||||
retry: false,
|
||||
meta: {
|
||||
disableToast: true,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from "react";
|
||||
import { useCurrentSettings } from "#/context/settings-context";
|
||||
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
|
||||
|
||||
export const useMigrateUserConsent = () => {
|
||||
const { saveUserSettings } = useCurrentSettings();
|
||||
|
||||
/**
|
||||
* Migrate user consent to the settings store on the server.
|
||||
*/
|
||||
const migrateUserConsent = React.useCallback(
|
||||
async (args?: { handleAnalyticsWasPresentInLocalStorage: () => void }) => {
|
||||
const userAnalyticsConsent = localStorage.getItem("analytics-consent");
|
||||
|
||||
if (userAnalyticsConsent) {
|
||||
args?.handleAnalyticsWasPresentInLocalStorage();
|
||||
|
||||
await saveUserSettings(
|
||||
{ user_consents_to_analytics: userAnalyticsConsent === "true" },
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleCaptureConsent(userAnalyticsConsent === "true");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
localStorage.removeItem("analytics-consent");
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return { migrateUserConsent };
|
||||
};
|
||||
+114
-121
@@ -3,7 +3,7 @@
|
||||
"en": "App",
|
||||
"ja": "アプリ",
|
||||
"zh-CN": "应用",
|
||||
"zh-TW": "應用程式",
|
||||
"zh-TW": "應用",
|
||||
"ko-KR": "앱",
|
||||
"no": "App",
|
||||
"it": "App",
|
||||
@@ -33,7 +33,7 @@
|
||||
"en": "If you tell OpenHands to start a web server, the app will appear here.",
|
||||
"ja": "OpenHandsにWebサーバーの起動を指示すると、ここにアプリが表示されます。",
|
||||
"zh-CN": "如果您告诉OpenHands启动Web服务器,应用将在此处显示。",
|
||||
"zh-TW": "如果您要求 OpenHands 啟動網頁伺服器,應用程式將會顯示在此處。",
|
||||
"zh-TW": "如果您告訴OpenHands啟動Web伺服器,應用將在此處顯示。",
|
||||
"ko-KR": "OpenHands에게 웹 서버를 시작하도록 지시하면 여기에 앱이 표시됩니다.",
|
||||
"no": "Hvis du ber OpenHands om å starte en webserver, vil appen vises her.",
|
||||
"it": "Se chiedi a OpenHands di avviare un server web, l'app apparirà qui.",
|
||||
@@ -138,7 +138,7 @@
|
||||
"en": "Waiting for client to become ready...",
|
||||
"ja": "クライアントの準備を待機中...",
|
||||
"zh-CN": "等待客户端准备就绪...",
|
||||
"zh-TW": "正在等待用戶端就緒...",
|
||||
"zh-TW": "等待客戶端準備就緒...",
|
||||
"ko-KR": "클라이언트 준비를 기다리는 중...",
|
||||
"de": "Warte auf Bereitschaft des Clients...",
|
||||
"no": "Venter på at klienten skal bli klar...",
|
||||
@@ -227,7 +227,7 @@
|
||||
"CODE_EDITOR$EMPTY_MESSAGE": {
|
||||
"en": "No file selected.",
|
||||
"zh-CN": "未选择文件",
|
||||
"zh-TW": "尚未選擇檔案",
|
||||
"zh-TW": "未選擇檔案",
|
||||
"de": "Keine Datei ausgewählt.",
|
||||
"ko-KR": "선택된 파일이 없습니다",
|
||||
"no": "Ingen fil valgt.",
|
||||
@@ -245,7 +245,7 @@
|
||||
"de": "Fehler beim Auswählen der Datei. Bitte versuchen Sie es erneut.",
|
||||
"ko-KR": "파일 선택 중 오류가 발생했습니다. 다시 시도해 주세요.",
|
||||
"no": "Feil ved valg av fil. Vennligst prøv igjen.",
|
||||
"zh-TW": "選擇檔案時發生錯誤,請再試一次",
|
||||
"zh-TW": "選擇檔案時出錯。請重試。",
|
||||
"it": "Errore durante la selezione del file. Riprova.",
|
||||
"pt": "Erro ao selecionar o arquivo. Por favor, tente novamente.",
|
||||
"es": "Error al seleccionar el archivo. Por favor, inténtelo de nuevo.",
|
||||
@@ -260,7 +260,7 @@
|
||||
"de": "Fehler beim Hochladen der Dateien. Bitte versuchen Sie es erneut.",
|
||||
"ko-KR": "파일 업로드 중 오류가 발생했습니다. 다시 시도해 주세요.",
|
||||
"no": "Feil ved opplasting av filer. Vennligst prøv igjen.",
|
||||
"zh-TW": "上傳檔案時發生錯誤,請再試一次",
|
||||
"zh-TW": "上傳檔案時出錯。請重試。",
|
||||
"it": "Errore durante il caricamento dei file. Riprova.",
|
||||
"pt": "Erro ao fazer upload dos arquivos. Por favor, tente novamente.",
|
||||
"es": "Error al subir los archivos. Por favor, inténtelo de nuevo.",
|
||||
@@ -275,7 +275,7 @@
|
||||
"de": "Fehler beim Auflisten der Dateien. Bitte versuchen Sie es erneut.",
|
||||
"ko-KR": "파일 목록을 가져오는 중 오류가 발생했습니다. 다시 시도해 주세요.",
|
||||
"no": "Feil ved listing av filer. Vennligst prøv igjen.",
|
||||
"zh-TW": "列出檔案時發生錯誤,請再試一次",
|
||||
"zh-TW": "列出檔案時出錯。請重試。",
|
||||
"it": "Errore durante l'elenco dei file. Riprova.",
|
||||
"pt": "Erro ao listar arquivos. Por favor, tente novamente.",
|
||||
"es": "Error al listar los archivos. Por favor, inténtelo de nuevo.",
|
||||
@@ -290,7 +290,7 @@
|
||||
"de": "Fehler beim Speichern der Datei. Bitte versuchen Sie es erneut.",
|
||||
"ko-KR": "파일 저장 중 오류가 발생했습니다. 다시 시도해 주세요.",
|
||||
"no": "Feil ved lagring av fil. Vennligst prøv igjen.",
|
||||
"zh-TW": "儲存檔案時發生錯誤,請再試一次",
|
||||
"zh-TW": "儲存檔案時出錯。請重試。",
|
||||
"it": "Errore durante il salvataggio del file. Riprova.",
|
||||
"pt": "Erro ao salvar o arquivo. Por favor, tente novamente.",
|
||||
"es": "Error al guardar el archivo. Por favor, inténtelo de nuevo.",
|
||||
@@ -318,7 +318,7 @@
|
||||
"en": "Auto-merge Dependabot PRs",
|
||||
"ja": "Dependabot PRを自動マージ",
|
||||
"zh-CN": "自动合并Dependabot PR",
|
||||
"zh-TW": "自動合併 Dependabot PR",
|
||||
"zh-TW": "自動合併Dependabot PR",
|
||||
"ko-KR": "Dependabot PR 자동 병합",
|
||||
"de": "Dependabot PRs automatisch zusammenführen",
|
||||
"no": "Auto-flett Dependabot PRs",
|
||||
@@ -333,7 +333,7 @@
|
||||
"en": "Improve README",
|
||||
"ja": "READMEを改善",
|
||||
"zh-CN": "改进README",
|
||||
"zh-TW": "改善 README",
|
||||
"zh-TW": "改進README",
|
||||
"ko-KR": "README 개선",
|
||||
"de": "README verbessern",
|
||||
"no": "Forbedre README",
|
||||
@@ -348,7 +348,7 @@
|
||||
"en": "Clean up dependencies",
|
||||
"ja": "依存関係を整理",
|
||||
"zh-CN": "清理依赖项",
|
||||
"zh-TW": "清理相依性",
|
||||
"zh-TW": "清理依賴項",
|
||||
"ko-KR": "의존성 정리",
|
||||
"de": "Abhängigkeiten bereinigen",
|
||||
"no": "Rydd opp i avhengigheter",
|
||||
@@ -365,7 +365,7 @@
|
||||
"de": "Ungültiger Dateipfad. Bitte überprüfen Sie den Dateinamen und versuchen Sie es erneut.",
|
||||
"ko-KR": "잘못된 파일 경로입니다. 파일 이름을 확인하고 다시 시도해 주세요.",
|
||||
"no": "Ugyldig filbane. Vennligst sjekk filnavnet og prøv igjen.",
|
||||
"zh-TW": "無效的檔案路徑,請檢查檔案名稱後再試一次",
|
||||
"zh-TW": "檔案路徑無效。請檢查檔案名稱並重試。",
|
||||
"it": "Percorso del file non valido. Controlla il nome del file e riprova.",
|
||||
"pt": "Caminho de arquivo inválido. Por favor, verifique o nome do arquivo e tente novamente.",
|
||||
"es": "Ruta de archivo inválida. Por favor, verifique el nombre del archivo e inténtelo de nuevo.",
|
||||
@@ -380,7 +380,7 @@
|
||||
"de": "Planer",
|
||||
"ko-KR": "플래너",
|
||||
"no": "Planlegger",
|
||||
"zh-TW": "規劃工具",
|
||||
"zh-TW": "規劃器",
|
||||
"ar": "المخطط",
|
||||
"fr": "Planificateur",
|
||||
"it": "Pianificatore",
|
||||
@@ -438,7 +438,7 @@
|
||||
"en": "Open in VS Code",
|
||||
"ja": "VS Codeで開く",
|
||||
"zh-CN": "在VS Code中打开",
|
||||
"zh-TW": "在 VS Code 中開啟",
|
||||
"zh-TW": "在VS Code中開啟",
|
||||
"ko-KR": "VS Code에서 열기",
|
||||
"de": "In VS Code öffnen",
|
||||
"no": "Åpne i VS Code",
|
||||
@@ -468,7 +468,7 @@
|
||||
"en": "Auto-merge PRs",
|
||||
"ja": "PRを自動マージ",
|
||||
"zh-CN": "自动合并PR",
|
||||
"zh-TW": "自動合併 PR",
|
||||
"zh-TW": "自動合併PR",
|
||||
"ko-KR": "PR 자동 병합",
|
||||
"de": "PRs automatisch zusammenführen",
|
||||
"no": "Auto-flett PRer",
|
||||
@@ -483,7 +483,7 @@
|
||||
"en": "Fix README",
|
||||
"ja": "READMEを修正",
|
||||
"zh-CN": "修复README",
|
||||
"zh-TW": "修正 README",
|
||||
"zh-TW": "修復README",
|
||||
"ko-KR": "README 수정",
|
||||
"de": "README korrigieren",
|
||||
"no": "Fiks README",
|
||||
@@ -515,7 +515,7 @@
|
||||
"de": "OpenHands Arbeitsverzeichnis",
|
||||
"ko-KR": "OpenHands 워크스페이스 디렉토리",
|
||||
"no": "OpenHands arbeidsmappe",
|
||||
"zh-TW": "OpenHands 工作區目錄",
|
||||
"zh-TW": "OpenHands工作區目錄",
|
||||
"it": "Directory dell'area di lavoro OpenHands",
|
||||
"pt": "Diretório do espaço de trabalho OpenHands",
|
||||
"es": "Directorio del espacio de trabajo de OpenHands",
|
||||
@@ -528,7 +528,7 @@
|
||||
"en": "LLM Provider",
|
||||
"ja": "LLMプロバイダー",
|
||||
"zh-CN": "LLM提供商",
|
||||
"zh-TW": "LLM 供應商",
|
||||
"zh-TW": "LLM提供者",
|
||||
"ko-KR": "LLM 제공자",
|
||||
"no": "LLM-leverandør",
|
||||
"it": "Provider LLM",
|
||||
@@ -543,7 +543,7 @@
|
||||
"en": "Select a provider",
|
||||
"ja": "プロバイダーを選択",
|
||||
"zh-CN": "选择提供商",
|
||||
"zh-TW": "選擇供應商",
|
||||
"zh-TW": "選擇提供者",
|
||||
"ko-KR": "제공자 선택",
|
||||
"no": "Velg en leverandør",
|
||||
"it": "Seleziona un provider",
|
||||
@@ -558,7 +558,7 @@
|
||||
"en": "API Key",
|
||||
"ja": "APIキー",
|
||||
"zh-CN": "API密钥",
|
||||
"zh-TW": "API 金鑰",
|
||||
"zh-TW": "API金鑰",
|
||||
"ko-KR": "API 키",
|
||||
"no": "API-nøkkel",
|
||||
"it": "Chiave API",
|
||||
@@ -573,7 +573,7 @@
|
||||
"en": "Don't know your API key?",
|
||||
"ja": "APIキーがわかりませんか?",
|
||||
"zh-CN": "不知道您的API密钥?",
|
||||
"zh-TW": "不知道您的 API 金鑰?",
|
||||
"zh-TW": "不知道您的API金鑰?",
|
||||
"ko-KR": "API 키를 모르시나요?",
|
||||
"no": "Kjenner du ikke API-nøkkelen din?",
|
||||
"it": "Non conosci la tua chiave API?",
|
||||
@@ -618,7 +618,7 @@
|
||||
"en": "Reset to defaults",
|
||||
"ja": "デフォルトにリセット",
|
||||
"zh-CN": "重置为默认值",
|
||||
"zh-TW": "還原為預設值",
|
||||
"zh-TW": "重置為預設值",
|
||||
"ko-KR": "기본값으로 재설정",
|
||||
"no": "Tilbakestill til standard",
|
||||
"it": "Ripristina valori predefiniti",
|
||||
@@ -648,7 +648,7 @@
|
||||
"en": "All information will be deleted",
|
||||
"ja": "すべての情報が削除されます",
|
||||
"zh-CN": "确定要重置所有设置吗?",
|
||||
"zh-TW": "所有資訊將會被刪除",
|
||||
"zh-TW": "確定要重置所有設定嗎?",
|
||||
"ko-KR": "모든 설정을 재설정하시겠습니까?",
|
||||
"no": "All informasjon vil bli slettet",
|
||||
"it": "Tutte le informazioni verranno eliminate",
|
||||
@@ -678,7 +678,7 @@
|
||||
"en": "Changing workspace settings will end the current session",
|
||||
"ja": "ワークスペース設定を変更すると、現在のセッションが終了します",
|
||||
"zh-CN": "确定要结束当前会话吗?",
|
||||
"zh-TW": "變更工作區設定將會結束目前的工作階段",
|
||||
"zh-TW": "確定要結束目前工作階段嗎?",
|
||||
"ko-KR": "현재 세션을 종료하시겠습니까?",
|
||||
"no": "Endring av arbeidsområdeinnstillinger vil avslutte gjeldende økt",
|
||||
"it": "La modifica delle impostazioni dell'area di lavoro terminerà la sessione corrente",
|
||||
@@ -738,7 +738,7 @@
|
||||
"en": "GitHub Token",
|
||||
"ja": "GitHubトークン",
|
||||
"zh-CN": "GitHub令牌",
|
||||
"zh-TW": "GitHub 權杖",
|
||||
"zh-TW": "GitHub權杖",
|
||||
"ko-KR": "GitHub 토큰",
|
||||
"no": "GitHub-token",
|
||||
"it": "Token GitHub",
|
||||
@@ -753,7 +753,7 @@
|
||||
"en": "GitHub Token (Optional)",
|
||||
"ja": "GitHubトークン(任意)",
|
||||
"zh-CN": "(可选)",
|
||||
"zh-TW": "GitHub 權杖(選填)",
|
||||
"zh-TW": "(選填)",
|
||||
"ko-KR": "(선택사항)",
|
||||
"no": "GitHub-token (valgfritt)",
|
||||
"it": "Token GitHub (opzionale)",
|
||||
@@ -798,7 +798,7 @@
|
||||
"en": "Enable analytics",
|
||||
"ja": "アナリティクスを有効にする",
|
||||
"zh-CN": "启用分析",
|
||||
"zh-TW": "啟用分析功能",
|
||||
"zh-TW": "啟用分析",
|
||||
"ko-KR": "분석 활성화",
|
||||
"no": "Aktiver analyse",
|
||||
"it": "Abilita analisi",
|
||||
@@ -813,7 +813,7 @@
|
||||
"en": "Invalid GitHub token",
|
||||
"ja": "GitHubトークンが無効です",
|
||||
"zh-CN": "GitHub令牌无效",
|
||||
"zh-TW": "GitHub 權杖無效",
|
||||
"zh-TW": "GitHub權杖無效",
|
||||
"ko-KR": "GitHub 토큰이 유효하지 않습니다",
|
||||
"no": "Ugyldig GitHub-token",
|
||||
"it": "Token GitHub non valido",
|
||||
@@ -843,7 +843,7 @@
|
||||
"en": "Configure Github Repositories",
|
||||
"ja": "GitHubリポジトリを設定",
|
||||
"zh-CN": "配置GitHub仓库",
|
||||
"zh-TW": "設定 GitHub 儲存庫",
|
||||
"zh-TW": "設定GitHub儲存庫",
|
||||
"ko-KR": "GitHub 저장소 설정",
|
||||
"no": "Konfigurer GitHub-repositorier",
|
||||
"it": "Configura repository GitHub",
|
||||
@@ -858,7 +858,7 @@
|
||||
"en": "Click here for instructions",
|
||||
"ja": "手順はこちらをクリック",
|
||||
"zh-CN": "点击查看说明",
|
||||
"zh-TW": "點此檢視說明",
|
||||
"zh-TW": "點擊查看說明",
|
||||
"ko-KR": "설명을 보려면 클릭하세요",
|
||||
"no": "Klikk her for instruksjoner",
|
||||
"it": "Clicca qui per le istruzioni",
|
||||
@@ -888,7 +888,7 @@
|
||||
"en": "LLM Model",
|
||||
"ja": "LLMモデル",
|
||||
"zh-CN": "LLM模型",
|
||||
"zh-TW": "LLM 模型",
|
||||
"zh-TW": "LLM模型",
|
||||
"ko-KR": "LLM 모델",
|
||||
"no": "LLM-modell",
|
||||
"it": "Modello LLM",
|
||||
@@ -905,7 +905,7 @@
|
||||
"de": "Standard: ./workspace",
|
||||
"ko-KR": "워크스페이스 디렉토리 경로 입력",
|
||||
"no": "Standard: ./workspace",
|
||||
"zh-TW": "預設:./workspace",
|
||||
"zh-TW": "輸入工作區目錄路徑",
|
||||
"ar": "الافتراضي: ./workspace",
|
||||
"fr": "Par défaut: ./workspace",
|
||||
"it": "Predefinito: ./workspace",
|
||||
@@ -950,7 +950,7 @@
|
||||
"de": "Wähle ein Modell",
|
||||
"ko-KR": "모델 선택",
|
||||
"no": "Velg en modell",
|
||||
"zh-TW": "請選擇模型",
|
||||
"zh-TW": "選擇模型",
|
||||
"it": "Seleziona un modello",
|
||||
"pt": "Selecione um modelo",
|
||||
"es": "Seleccionar un modelo",
|
||||
@@ -965,7 +965,7 @@
|
||||
"de": "Agent",
|
||||
"ko-KR": "에이전트",
|
||||
"no": "Agent",
|
||||
"zh-TW": "智慧代理",
|
||||
"zh-TW": "代理",
|
||||
"it": "Agente",
|
||||
"pt": "Agente",
|
||||
"es": "Agente",
|
||||
@@ -980,7 +980,7 @@
|
||||
"de": "Wähle einen Agenten",
|
||||
"ko-KR": "에이전트 선택",
|
||||
"no": "Velg en agent",
|
||||
"zh-TW": "請選擇智慧代理",
|
||||
"zh-TW": "選擇代理",
|
||||
"it": "Seleziona un agente",
|
||||
"pt": "Selecione um agente",
|
||||
"es": "Seleccionar un agente",
|
||||
@@ -1010,7 +1010,7 @@
|
||||
"de": "Wähle eine Sprache",
|
||||
"ko-KR": "언어 선택",
|
||||
"no": "Velg et språk",
|
||||
"zh-TW": "請選擇語言",
|
||||
"zh-TW": "選擇語言",
|
||||
"it": "Seleziona una lingua",
|
||||
"pt": "Selecione um idioma",
|
||||
"es": "Seleccionar un idioma",
|
||||
@@ -1025,7 +1025,7 @@
|
||||
"zh-CN": "安全性",
|
||||
"ko-KR": "보안",
|
||||
"no": "Sikkerhetsanalysator",
|
||||
"zh-TW": "安全性分析工具",
|
||||
"zh-TW": "安全性",
|
||||
"it": "Analizzatore di sicurezza",
|
||||
"pt": "Analisador de segurança",
|
||||
"es": "Analizador de seguridad",
|
||||
@@ -1040,7 +1040,7 @@
|
||||
"zh-CN": "选择安全级别",
|
||||
"ko-KR": "보안 수준 선택",
|
||||
"no": "Velg en sikkerhetsanalysator (valgfritt)",
|
||||
"zh-TW": "請選擇安全性分析工具(選填)",
|
||||
"zh-TW": "選擇安全等級",
|
||||
"it": "Seleziona un analizzatore di sicurezza (opzionale)",
|
||||
"pt": "Selecione um analisador de segurança (opcional)",
|
||||
"es": "Seleccione un analizador de seguridad (opcional)",
|
||||
@@ -1085,7 +1085,7 @@
|
||||
"de": "Auf Standardwerte zurücksetzen",
|
||||
"ko-KR": "재설정",
|
||||
"no": "Tilbakestill til standardverdier",
|
||||
"zh-TW": "還原為預設值",
|
||||
"zh-TW": "重置",
|
||||
"it": "Reimposta ai valori predefiniti",
|
||||
"pt": "Redefinir para os padrões",
|
||||
"es": "Restablecer valores predeterminados",
|
||||
@@ -1205,7 +1205,7 @@
|
||||
"zh-CN": "设置需要更新",
|
||||
"ko-KR": "설정 업데이트 필요",
|
||||
"no": "Vi har endret noen innstillinger i den siste oppdateringen. Ta deg tid til å se gjennom dem.",
|
||||
"zh-TW": "我們在最新的更新中改變了一些設定,請花一些時間檢視這些更新。",
|
||||
"zh-TW": "設定需要更新",
|
||||
"it": "Abbiamo modificato alcune impostazioni nell'ultimo aggiornamento. Prenditi un momento per rivederle.",
|
||||
"pt": "Alteramos algumas configurações na última atualização. Reserve um momento para revisar.",
|
||||
"es": "Hemos cambiado algunas configuraciones en la última actualización. Tómate un momento para revisarlas.",
|
||||
@@ -1220,7 +1220,7 @@
|
||||
"zh-CN": "代理加载中",
|
||||
"ko-KR": "에이전트 로딩 중",
|
||||
"no": "Vennligst vent mens agenten laster. Dette kan ta noen minutter...",
|
||||
"zh-TW": "請稍待智慧代理載入,這可能需要幾分鐘的時間...",
|
||||
"zh-TW": "代理載入中",
|
||||
"it": "Attendere mentre l'agente si carica. Potrebbe richiedere alcuni minuti...",
|
||||
"pt": "Por favor, aguarde enquanto o agente carrega. Isso pode levar alguns minutos...",
|
||||
"es": "Por favor, espere mientras el agente se carga. Esto puede tardar unos minutos...",
|
||||
@@ -1235,7 +1235,7 @@
|
||||
"zh-CN": "代理运行中",
|
||||
"ko-KR": "에이전트 실행 중",
|
||||
"no": "Vennligst stopp agenten før du redigerer disse innstillingene.",
|
||||
"zh-TW": "請先停止智慧代理再編輯這些設定。",
|
||||
"zh-TW": "代理執行中",
|
||||
"it": "Si prega di fermare l'agente prima di modificare queste impostazioni.",
|
||||
"pt": "Por favor, pare o agente antes de editar estas configurações.",
|
||||
"es": "Por favor, detenga el agente antes de editar estas configuraciones.",
|
||||
@@ -1248,7 +1248,7 @@
|
||||
"en": "Failed to fetch models and agents",
|
||||
"zh-CN": "获取模型时出错",
|
||||
"de": "Fehler beim Abrufen der Modelle und Agenten",
|
||||
"zh-TW": "取得模型與智慧代理失敗",
|
||||
"zh-TW": "取得模型時出錯",
|
||||
"es": "Error al obtener modelos y agentes",
|
||||
"fr": "Échec de la récupération des modèles et des agents",
|
||||
"it": "Impossibile recuperare modelli e agenti",
|
||||
@@ -1261,13 +1261,11 @@
|
||||
},
|
||||
"CONFIGURATION$SETTINGS_NOT_FOUND": {
|
||||
"en": "Settings not found. Please check your API key",
|
||||
"es": "Configuraciones no encontradas. Por favor revisa tu API key",
|
||||
"zh-TW": "找不到設定。請檢查您的 API 金鑰"
|
||||
"es": "Configuraciones no encontradas. Por favor revisa tu API key"
|
||||
},
|
||||
"CONNECT_TO_GITHUB_BY_TOKEN_MODAL$TERMS_OF_SERVICE": {
|
||||
"en": "terms of service",
|
||||
"es": "términos de servicio",
|
||||
"zh-TW": "服務條款"
|
||||
"es": "términos de servicio"
|
||||
},
|
||||
"SESSION$SERVER_CONNECTED_MESSAGE": {
|
||||
"en": "Connected to server",
|
||||
@@ -1287,7 +1285,7 @@
|
||||
"en": "Error handling message",
|
||||
"zh-CN": "处理会话时出错",
|
||||
"de": "Fehler beim Verarbeiten der Nachricht",
|
||||
"zh-TW": "處理工作階段時發生錯誤",
|
||||
"zh-TW": "處理工作階段時出錯",
|
||||
"es": "Error al procesar el mensaje",
|
||||
"fr": "Erreur lors du traitement du message",
|
||||
"it": "Errore durante l'elaborazione del messaggio",
|
||||
@@ -1302,7 +1300,7 @@
|
||||
"en": "Error connecting to session",
|
||||
"zh-CN": "连接会话时出错",
|
||||
"de": "Verbindung zur Sitzung fehlgeschlagen",
|
||||
"zh-TW": "連線工作階段時發生錯誤",
|
||||
"zh-TW": "連線工作階段時出錯",
|
||||
"es": "Error al conectar con la sesión",
|
||||
"fr": "Erreur de connexion à la session",
|
||||
"it": "Errore durante la connessione alla sessione",
|
||||
@@ -1332,7 +1330,7 @@
|
||||
"en": "Error uploading file",
|
||||
"zh-CN": "上传时出错",
|
||||
"de": "Fehler beim Hochladen der Datei",
|
||||
"zh-TW": "上傳時發生錯誤",
|
||||
"zh-TW": "上傳時出錯",
|
||||
"es": "Error al subir el archivo",
|
||||
"fr": "Erreur lors du téléchargement du fichier",
|
||||
"it": "Errore durante il caricamento del file",
|
||||
@@ -1407,7 +1405,7 @@
|
||||
"en": "Error refreshing workspace",
|
||||
"zh-CN": "刷新时出错",
|
||||
"de": "Fehler beim Aktualisieren des Arbeitsbereichs",
|
||||
"zh-TW": "重新整理時發生錯誤",
|
||||
"zh-TW": "重新整理時出錯",
|
||||
"es": "Error al actualizar el espacio de trabajo",
|
||||
"fr": "Erreur lors de l'actualisation de l'espace de travail",
|
||||
"it": "Errore durante l'aggiornamento dell'area di lavoro",
|
||||
@@ -1481,7 +1479,7 @@
|
||||
"EXPLORER$VSCODE_SWITCHING_MESSAGE": {
|
||||
"en": "Switching to VS Code in 3 seconds...\nImportant: Please inform the agent of any changes you make in VS Code. To avoid conflicts, wait for the assistant to complete its work before making your own changes.",
|
||||
"zh-CN": "切换到VS Code中...",
|
||||
"zh-TW": "切換到 VS Code 中...",
|
||||
"zh-TW": "切換到VS Code中...",
|
||||
"ja": "3秒後にVS Codeに切り替わります...\n重要:VS Codeで行った変更はエージェントに通知してください。競合を避けるため、アシスタントの作業が完了するまで自身の変更を待ってください。",
|
||||
"ko-KR": "VS Code로 전환 중...",
|
||||
"no": "Bytter til VS Code om 3 sekunder...\nViktig: Vennligst informer agenten om eventuelle endringer du gjør i VS Code. For å unngå konflikter, vent til assistenten er ferdig med sitt arbeid før du gjør dine egne endringer.",
|
||||
@@ -1496,7 +1494,7 @@
|
||||
"EXPLORER$VSCODE_SWITCHING_ERROR_MESSAGE": {
|
||||
"en": "Error switching to VS Code: {{error}}",
|
||||
"zh-CN": "切换到VS Code时出错",
|
||||
"zh-TW": "切換到 VS Code 時發生錯誤",
|
||||
"zh-TW": "切換到VS Code時出錯",
|
||||
"ja": "VS Codeへの切り替え中にエラーが発生しました: {{error}}",
|
||||
"ko-KR": "VS Code로 전환 중 오류 발생",
|
||||
"no": "Feil ved bytte til VS Code: {{error}}",
|
||||
@@ -1512,7 +1510,7 @@
|
||||
"en": "Return to existing session?",
|
||||
"de": "Zurück zu vorhandener Sitzung?",
|
||||
"zh-CN": "是否继续未完成的会话?",
|
||||
"zh-TW": "是否繼續未完成的會話?",
|
||||
"zh-TW": "是否繼續未完成的會話?",
|
||||
"es": "¿Volver a la sesión existente?",
|
||||
"fr": "Revenir à la session existante ?",
|
||||
"it": "Tornare alla sessione esistente?",
|
||||
@@ -1572,7 +1570,7 @@
|
||||
"en": "Share feedback",
|
||||
"de": "Feedback teilen",
|
||||
"zh-CN": "分享反馈",
|
||||
"zh-TW": "分享回饋",
|
||||
"zh-TW": "分享反饋",
|
||||
"es": "Compartir comentarios",
|
||||
"fr": "Partager des commentaires",
|
||||
"it": "Condividi feedback",
|
||||
@@ -1587,7 +1585,7 @@
|
||||
"en": "To help us improve, we collect feedback from your interactions to improve our prompts. By submitting this form, you consent to us collecting this data.",
|
||||
"de": "Um uns zu verbessern, sammeln wir Feedback aus Ihren Interaktionen, um unsere Prompts zu verbessern. Durch das Absenden dieses Formulars stimmen Sie der Erfassung dieser Daten zu.",
|
||||
"zh-CN": "为了帮助我们改进,我们会收集您的互动反馈以改进我们的提示。提交此表单即表示您同意我们收集这些数据。",
|
||||
"zh-TW": "為了幫助我們改進,我們會收集您的互動回饋以改進我們的提示。提交此表單即表示您同意我們收集這些資料。",
|
||||
"zh-TW": "為了幫助我們改進,我們會收集您的互動反饋以改進我們的提示。提交此表單即表示您同意我們收集這些數據。",
|
||||
"es": "Para ayudarnos a mejorar, recopilamos comentarios de sus interacciones para mejorar nuestras indicaciones. Al enviar este formulario, usted consiente que recopilemos estos datos.",
|
||||
"fr": "Pour nous aider à nous améliorer, nous recueillons des commentaires de vos interactions pour améliorer nos invites. En soumettant ce formulaire, vous consentez à ce que nous collections ces données.",
|
||||
"it": "Per aiutarci a migliorare, raccogliamo feedback dalle tue interazioni per migliorare i nostri prompt. Inviando questo modulo, acconsenti alla raccolta di questi dati.",
|
||||
@@ -1617,7 +1615,7 @@
|
||||
"en": "Contribute to public dataset",
|
||||
"de": "Zum öffentlichen Datensatz beitragen",
|
||||
"zh-CN": "贡献到公共数据集",
|
||||
"zh-TW": "貢獻到公共資料集",
|
||||
"zh-TW": "貢獻到公共數據集",
|
||||
"es": "Contribuir al conjunto de datos público",
|
||||
"fr": "Contribuer à l'ensemble de données public",
|
||||
"it": "Contribuisci al dataset pubblico",
|
||||
@@ -1677,7 +1675,7 @@
|
||||
"en": "Password copied to clipboard.",
|
||||
"es": "Contraseña copiada al portapapeles.",
|
||||
"zh-CN": "密码已复制到剪贴板。",
|
||||
"zh-TW": "密碼已複製到剪貼簿。",
|
||||
"zh-TW": "密碼已複製到剪貼板。",
|
||||
"ko-KR": "비밀번호가 클립보드에 복사되었습니다.",
|
||||
"no": "Passord kopiert til utklippstavlen.",
|
||||
"ar": "تم نسخ كلمة المرور إلى الحافظة.",
|
||||
@@ -1692,7 +1690,7 @@
|
||||
"en": "Go to shared feedback",
|
||||
"es": "Ir a feedback compartido",
|
||||
"zh-CN": "转到共享反馈",
|
||||
"zh-TW": "前往分享回饋",
|
||||
"zh-TW": "前往共享反饋",
|
||||
"ko-KR": "공유된 피드백으로 이동",
|
||||
"no": "Gå til delt tilbakemelding",
|
||||
"ar": "الذهاب إلى التعليقات المشتركة",
|
||||
@@ -1737,7 +1735,7 @@
|
||||
"en": "Failed to share, please contact the developers:",
|
||||
"es": "Error al compartir, por favor contacta con los desarrolladores:",
|
||||
"zh-CN": "分享失败,请联系开发人员:",
|
||||
"zh-TW": "分享失敗,請聯絡開發人員:",
|
||||
"zh-TW": "分享失敗,請聯繫開發人員:",
|
||||
"ko-KR": "공유 실패, 개발자에게 문의하세요:",
|
||||
"no": "Deling mislyktes, vennligst kontakt utviklerne:",
|
||||
"ar": "فشل المشاركة، يرجى الاتصال بالمطورين:",
|
||||
@@ -1842,7 +1840,7 @@
|
||||
"en": "Ask for user confirmation on risk severity:",
|
||||
"es": "Preguntar por confirmación del usuario sobre severidad del riesgo:",
|
||||
"zh-CN": "询问用户确认风险等级:",
|
||||
"zh-TW": "詢問使用者確認風險等級:",
|
||||
"zh-TW": "詢問用戶確認風險等級:",
|
||||
"ko-KR": "위험 심각도에 대한 사용자 확인 요청:",
|
||||
"no": "Be om brukerbekreftelse på risikoalvorlighet:",
|
||||
"ar": "اطلب تأكيد المستخدم على مستوى الخطورة:",
|
||||
@@ -1902,7 +1900,7 @@
|
||||
"en": "Click to learn more",
|
||||
"es": "Clic para aprender más",
|
||||
"zh-CN": "点击了解更多",
|
||||
"zh-TW": "點選了解更多",
|
||||
"zh-TW": "點擊了解更多",
|
||||
"ko-KR": "자세히 알아보기",
|
||||
"no": "Klikk for å lære mer",
|
||||
"ar": "انقر لمعرفة المزيد",
|
||||
@@ -2022,7 +2020,7 @@
|
||||
"en": "Agent is initialized, waiting for task...",
|
||||
"de": "Agent ist initialisiert und wartet auf Aufgabe...",
|
||||
"zh-CN": "智能体已初始化,等待任务中...",
|
||||
"zh-TW": "智慧代理已初始化,等待任務中...",
|
||||
"zh-TW": "智能體已初始化,等待任務中...",
|
||||
"ko-KR": "에이전트가 초기화되었습니다. 작업을 기다리는 중...",
|
||||
"no": "Agenten er initialisert, venter på oppgave...",
|
||||
"it": "L'agente è inizializzato, in attesa di compiti...",
|
||||
@@ -2037,7 +2035,7 @@
|
||||
"en": "Agent is running task",
|
||||
"de": "Agent führt Aufgabe aus",
|
||||
"zh-CN": "智能体正在执行任务...",
|
||||
"zh-TW": "智慧代理正在執行任務...",
|
||||
"zh-TW": "智能體正在執行任務...",
|
||||
"ko-KR": "에이전트가 작업을 실행 중입니다",
|
||||
"no": "Agenten utfører oppgave",
|
||||
"it": "L'agente sta eseguendo il compito",
|
||||
@@ -2052,7 +2050,7 @@
|
||||
"en": "Agent is awaiting user input...",
|
||||
"de": "Agent wartet auf Benutzereingabe...",
|
||||
"zh-CN": "智能体正在等待用户输入...",
|
||||
"zh-TW": "智慧代理正在等待使用者輸入...",
|
||||
"zh-TW": "智能體正在等待用戶輸入...",
|
||||
"ko-KR": "에이전트가 사용자 입력을 기다리고 있습니다...",
|
||||
"no": "Agenten venter på brukerinndata...",
|
||||
"it": "L'agente è in attesa dell'input dell'utente...",
|
||||
@@ -2066,7 +2064,7 @@
|
||||
"CHAT_INTERFACE$AGENT_RATE_LIMITED_MESSAGE": {
|
||||
"en": "Agent is Rate Limited",
|
||||
"zh-CN": "智能体已达到速率限制",
|
||||
"zh-TW": "智慧代理已達到速率限制",
|
||||
"zh-TW": "智能體已達到速率限制",
|
||||
"de": "Agent ist ratenbegrenzt",
|
||||
"ko-KR": "에이전트가 속도 제한되었습니다",
|
||||
"no": "Agenten er hastighetsbegrenset",
|
||||
@@ -2082,7 +2080,7 @@
|
||||
"en": "Agent has paused.",
|
||||
"de": "Agent pausiert.",
|
||||
"zh-CN": "智能体已暂停",
|
||||
"zh-TW": "智慧代理已暫停",
|
||||
"zh-TW": "智能體已暫停",
|
||||
"ko-KR": "에이전트가 일시 중지되었습니다.",
|
||||
"no": "Agenten har pauset.",
|
||||
"it": "L'agente ha messo in pausa.",
|
||||
@@ -2097,7 +2095,7 @@
|
||||
"en": "Let's start building!",
|
||||
"ja": "開発を始めましょう!",
|
||||
"zh-CN": "让我们开始构建!",
|
||||
"zh-TW": "讓我們開始建構!",
|
||||
"zh-TW": "讓我們開始構建!",
|
||||
"ko-KR": "시작해봅시다!",
|
||||
"fr": "Commençons à construire !",
|
||||
"es": "¡Empecemos a construir!",
|
||||
@@ -2112,7 +2110,7 @@
|
||||
"en": "OpenHands makes it easy to build and maintain software using a simple prompt.",
|
||||
"ja": "OpenHandsは、シンプルなプロンプトを使用してソフトウェアの開発と保守を簡単にします。",
|
||||
"zh-CN": "OpenHands 使用简单的提示来轻松构建和维护软件。",
|
||||
"zh-TW": "OpenHands 使用簡單的提示來輕鬆建構和維護軟體。",
|
||||
"zh-TW": "OpenHands 使用簡單的提示來輕鬆構建和維護軟件。",
|
||||
"ko-KR": "OpenHands는 간단한 프롬프트를 사용하여 소프트웨어를 쉽게 구축하고 유지관리할 수 있게 해줍니다.",
|
||||
"fr": "OpenHands facilite la construction et la maintenance de logiciels à l'aide d'une invite simple.",
|
||||
"es": "OpenHands facilita la construcción y el mantenimiento de software usando un prompt simple.",
|
||||
@@ -2157,7 +2155,7 @@
|
||||
"en": "Create a Hello World app",
|
||||
"ja": "Hello World アプリを作成",
|
||||
"zh-CN": "创建一个 Hello World 应用",
|
||||
"zh-TW": "建立一個 Hello World 應用程式",
|
||||
"zh-TW": "創建一個 Hello World 應用",
|
||||
"ko-KR": "Hello World 앱 만들기",
|
||||
"fr": "Créer une application Hello World",
|
||||
"es": "Crear una aplicación Hello World",
|
||||
@@ -2172,7 +2170,7 @@
|
||||
"en": "Build a todo list application",
|
||||
"ja": "ToDoリストアプリを開発する",
|
||||
"zh-CN": "构建一个待办事项列表应用",
|
||||
"zh-TW": "建構一個待辦事項列表應用程式",
|
||||
"zh-TW": "構建一個待辦事項列表應用",
|
||||
"ko-KR": "할 일 목록 앱 만들기",
|
||||
"fr": "Construire une application de liste de tâches",
|
||||
"es": "Construir una aplicación de lista de tareas",
|
||||
@@ -2187,7 +2185,7 @@
|
||||
"en": "Write a bash script that shows the top story on Hacker News",
|
||||
"ja": "Hacker Newsのトップ記事を表示するbashスクリプトを作成する",
|
||||
"zh-CN": "编写一个显示Hacker News头条新闻的bash脚本",
|
||||
"zh-TW": "編寫一個顯示 Hacker News 頭條新聞的 bash 腳本",
|
||||
"zh-TW": "編寫一個顯示Hacker News頭條新聞的bash腳本",
|
||||
"ko-KR": "Hacker News의 상위 기사를 보여주는 bash 스크립트 작성하기",
|
||||
"fr": "Écrire un script bash qui affiche l'article principal de Hacker News",
|
||||
"es": "Escribir un script bash que muestre la noticia principal de Hacker News",
|
||||
@@ -2217,7 +2215,7 @@
|
||||
"en": "Connect to GitHub",
|
||||
"ja": "GitHubに接続",
|
||||
"zh-CN": "连接到GitHub",
|
||||
"zh-TW": "連線到 GitHub",
|
||||
"zh-TW": "連接到GitHub",
|
||||
"ko-KR": "GitHub에 연결",
|
||||
"fr": "Se connecter à GitHub",
|
||||
"es": "Conectar con GitHub",
|
||||
@@ -2247,7 +2245,7 @@
|
||||
"en": "Add more repositories...",
|
||||
"ja": "リポジトリを追加...",
|
||||
"zh-CN": "添加更多仓库...",
|
||||
"zh-TW": "新增更多儲存庫...",
|
||||
"zh-TW": "添加更多儲存庫...",
|
||||
"ko-KR": "더 많은 저장소 추가...",
|
||||
"fr": "Ajouter plus de dépôts...",
|
||||
"es": "Agregar más repositorios...",
|
||||
@@ -2427,7 +2425,7 @@
|
||||
"en": "Resume the agent task",
|
||||
"ja": "エージェントのタスクを再開",
|
||||
"zh-CN": "恢复代理任务",
|
||||
"zh-TW": "恢復智慧代理任務",
|
||||
"zh-TW": "恢復代理任務",
|
||||
"ko-KR": "에이전트 작업 재개",
|
||||
"fr": "Reprendre la tâche de l'agent",
|
||||
"es": "Reanudar la tarea del agente",
|
||||
@@ -2442,7 +2440,7 @@
|
||||
"en": "Pause the current task",
|
||||
"ja": "現在のタスクを一時停止",
|
||||
"zh-CN": "暂停当前任务",
|
||||
"zh-TW": "暫停目前任務",
|
||||
"zh-TW": "暫停當前任務",
|
||||
"ko-KR": "현재 작업 일시 중지",
|
||||
"fr": "Mettre en pause la tâche actuelle",
|
||||
"es": "Pausar la tarea actual",
|
||||
@@ -2577,7 +2575,7 @@
|
||||
"en": "Upload a .zip",
|
||||
"ja": ".zipファイルをアップロード",
|
||||
"zh-CN": "上传.zip文件",
|
||||
"zh-TW": "上傳 .zip 檔案",
|
||||
"zh-TW": "上傳.zip檔案",
|
||||
"ko-KR": ".zip 파일 업로드",
|
||||
"fr": "Télécharger un .zip",
|
||||
"es": "Subir un .zip",
|
||||
@@ -2637,7 +2635,7 @@
|
||||
"en": "Auto-merge Dependabot PRs",
|
||||
"ja": "DependabotのPRを自動マージ",
|
||||
"zh-CN": "自动合并Dependabot PR",
|
||||
"zh-TW": "自動合併 Dependabot PR",
|
||||
"zh-TW": "自動合併Dependabot PR",
|
||||
"ko-KR": "Dependabot PR 자동 병합",
|
||||
"fr": "Fusion automatique des PR Dependabot",
|
||||
"es": "Auto-fusionar PRs de Dependabot",
|
||||
@@ -2652,7 +2650,7 @@
|
||||
"en": "Agent has stopped.",
|
||||
"de": "Agent hat angehalten.",
|
||||
"zh-CN": "智能体已停止",
|
||||
"zh-TW": "智慧代理已停止",
|
||||
"zh-TW": "智能體已停止",
|
||||
"ko-KR": "에이전트가 중지되었습니다.",
|
||||
"no": "Agenten har stoppet.",
|
||||
"it": "L'agente si è fermato.",
|
||||
@@ -2667,7 +2665,7 @@
|
||||
"en": "Agent has finished the task.",
|
||||
"de": "Agent hat die Aufgabe erledigt.",
|
||||
"zh-CN": "智能体已完成任务",
|
||||
"zh-TW": "智慧代理已完成任務",
|
||||
"zh-TW": "智能體已完成任務",
|
||||
"ko-KR": "에이전트가 작업을 완료했습니다.",
|
||||
"no": "Agenten har fullført oppgaven.",
|
||||
"it": "L'agente ha completato il compito.",
|
||||
@@ -2682,7 +2680,7 @@
|
||||
"en": "Agent has rejected the task.",
|
||||
"de": "Agent hat die Aufgabe abgelehnt.",
|
||||
"zh-CN": "智能体拒绝任务",
|
||||
"zh-TW": "智慧代理拒絕任務",
|
||||
"zh-TW": "智能體拒絕任務",
|
||||
"ko-KR": "에이전트가 작업을 거부했습니다.",
|
||||
"no": "Agenten har avvist oppgaven.",
|
||||
"it": "L'agente ha rifiutato il compito.",
|
||||
@@ -2697,7 +2695,7 @@
|
||||
"en": "Agent encountered an error.",
|
||||
"de": "Agent ist auf einen Fehler gelaufen.",
|
||||
"zh-CN": "智能体遇到错误",
|
||||
"zh-TW": "智慧代理遇到錯誤",
|
||||
"zh-TW": "智能體遇到錯誤",
|
||||
"ko-KR": "에이전트에 오류가 발생했습니다.",
|
||||
"no": "Agenten støtte på en feil.",
|
||||
"it": "L'agente ha riscontrato un errore.",
|
||||
@@ -2712,7 +2710,7 @@
|
||||
"en": "Agent is awaiting user confirmation for the pending action.",
|
||||
"de": "Agent wartet auf die Bestätigung des Benutzers für die ausstehende Aktion.",
|
||||
"zh-CN": "代理正在等待用户确认待处理的操作。",
|
||||
"zh-TW": "智慧代理正在等待用戶確認待處理的操作。",
|
||||
"zh-TW": "代理正在等待用戶確認待處理的操作。",
|
||||
"ko-KR": "에이전트가 대기 중인 작업에 대한 사용자 확인을 기다리고 있습니다.",
|
||||
"no": "Agenten venter på brukerbekreftelse for den ventende handlingen.",
|
||||
"it": "L'agente è in attesa della conferma dell'utente per l'azione in sospeso.",
|
||||
@@ -2727,7 +2725,7 @@
|
||||
"en": "Agent action has been confirmed!",
|
||||
"de": "Die Aktion des Agenten wurde bestätigt!",
|
||||
"zh-CN": "代理操作已确认!",
|
||||
"zh-TW": "智慧代理操作已確認!",
|
||||
"zh-TW": "代理操作已確認!",
|
||||
"ko-KR": "에이전트 작업이 확인되었습니다!",
|
||||
"no": "Agenthandlingen har blitt bekreftet!",
|
||||
"it": "L'azione dell'agente è stata confermata!",
|
||||
@@ -2742,7 +2740,7 @@
|
||||
"en": "Agent action has been rejected!",
|
||||
"de": "Die Aktion des Agenten wurde abgelehnt!",
|
||||
"zh-CN": "代理操作已被拒绝!",
|
||||
"zh-TW": "智慧代理操作已被拒絕!",
|
||||
"zh-TW": "代理操作已被拒絕!",
|
||||
"ko-KR": "에이전트 작업이 거부되었습니다!",
|
||||
"no": "Agenthandlingen har blitt avvist!",
|
||||
"it": "L'azione dell'agente è stata rifiutata!",
|
||||
@@ -2759,7 +2757,7 @@
|
||||
"de": "Sende eine Nachricht an den Assistenten...",
|
||||
"ko-KR": "어시스턴트에게 메시지 보내기",
|
||||
"no": "Send melding til assistenten...",
|
||||
"zh-TW": "給助理留言",
|
||||
"zh-TW": "给助理留言",
|
||||
"it": "Invia un messaggio all'assistente...",
|
||||
"pt": "Envie uma mensagem para o assistente...",
|
||||
"es": "Mensaje al asistente...",
|
||||
@@ -2834,7 +2832,7 @@
|
||||
"de": "Senden",
|
||||
"ko-KR": "전송",
|
||||
"no": "Send",
|
||||
"zh-TW": "傳送",
|
||||
"zh-TW": "發送",
|
||||
"it": "Invia",
|
||||
"pt": "Enviar",
|
||||
"es": "Enviar",
|
||||
@@ -2894,7 +2892,7 @@
|
||||
"de": "Nachricht senden",
|
||||
"ko-KR": "메시지 보내기",
|
||||
"no": "Send melding",
|
||||
"zh-TW": "傳送訊息",
|
||||
"zh-TW": "發送訊息",
|
||||
"it": "Invia messaggio",
|
||||
"pt": "Enviar mensagem",
|
||||
"es": "Enviar mensaje",
|
||||
@@ -2954,7 +2952,7 @@
|
||||
"zh-CN": "回到底部",
|
||||
"ko-KR": "맨 아래로",
|
||||
"no": "Til bunnen",
|
||||
"zh-TW": "回到底端",
|
||||
"zh-TW": "回到底部",
|
||||
"it": "In fondo",
|
||||
"pt": "Para o fundo",
|
||||
"es": "Ir al final",
|
||||
@@ -3086,7 +3084,7 @@
|
||||
"SETTINGS$AGENT_TOOLTIP": {
|
||||
"en": "Select the agent to use.",
|
||||
"zh-CN": "选择要使用的智能体",
|
||||
"zh-TW": "選擇要使用的智慧代理。",
|
||||
"zh-TW": "選擇要使用的智能體。",
|
||||
"de": "Wähle den zu verwendenden Agenten.",
|
||||
"ko-KR": "사용할 에이전트를 선택하세요.",
|
||||
"no": "Velg agenten som skal brukes.",
|
||||
@@ -3116,7 +3114,7 @@
|
||||
"SETTINGS$DISABLED_RUNNING": {
|
||||
"en": "Cannot be changed while the agent is running.",
|
||||
"zh-CN": "在智能体运行时无法更改",
|
||||
"zh-TW": "智慧代理正在執行時無法更改。",
|
||||
"zh-TW": "智能體正在執行時無法更改。",
|
||||
"de": "Kann bei laufender Aufgabe nicht geändert werden.",
|
||||
"ko-KR": "에이전트가 실행 중일 때는 변경할 수 없습니다.",
|
||||
"no": "Kan ikke endres mens agenten kjører.",
|
||||
@@ -3176,7 +3174,7 @@
|
||||
"SETTINGS$AGENT_SELECT_ENABLED": {
|
||||
"en": "Enable Agent Selection - Advanced Users",
|
||||
"zh-CN": "启用智能体选择 - 高级用户",
|
||||
"zh-TW": "啟用智慧代理選擇 - 進階使用者",
|
||||
"zh-TW": "啟用智能體選擇 - 進階使用者",
|
||||
"de": "Agentenauswahl aktivieren - Fortgeschrittene Benutzer",
|
||||
"ko-KR": "에이전트 선택 활성화 - 고급 사용자",
|
||||
"no": "Aktiver agentvalg - Avanserte brukere",
|
||||
@@ -3206,7 +3204,7 @@
|
||||
"PLANNER$EMPTY_MESSAGE": {
|
||||
"en": "No plan created.",
|
||||
"zh-CN": "计划未创建",
|
||||
"zh-TW": "未建立任何計劃。",
|
||||
"zh-TW": "未創建任何計劃。",
|
||||
"de": "Kein Plan erstellt.",
|
||||
"ko-KR": "생성된 계획이 없습니다.",
|
||||
"no": "Ingen plan opprettet.",
|
||||
@@ -3251,7 +3249,7 @@
|
||||
"STATUS$STARTING_RUNTIME": {
|
||||
"en": "Starting Runtime...",
|
||||
"zh-CN": "启动运行时...",
|
||||
"zh-TW": "啟動執行時...",
|
||||
"zh-TW": "啟動運行時...",
|
||||
"de": "Laufzeitumgebung wird gestartet...",
|
||||
"ko-KR": "런타임 시작 중...",
|
||||
"no": "Starter kjøretidsmiljø...",
|
||||
@@ -3357,7 +3355,7 @@
|
||||
"en": "GitHub token is invalid. Please try again.",
|
||||
"es": "El token de GitHub no es válido. Por favor, inténtelo de nuevo.",
|
||||
"zh-CN": "GitHub令牌无效。请重试。",
|
||||
"zh-TW": "GitHub 權杖無效",
|
||||
"zh-TW": "GitHub權杖無效",
|
||||
"ko-KR": "GitHub 토큰이 유효하지 않습니다",
|
||||
"ja": "GitHubトークンが無効です",
|
||||
"no": "GitHub-token er ugyldig. Vennligst prøv igjen.",
|
||||
@@ -3507,7 +3505,7 @@
|
||||
"en": "Base URL",
|
||||
"es": "URL base",
|
||||
"zh-CN": "基础URL",
|
||||
"zh-TW": "基礎 URL",
|
||||
"zh-TW": "基礎URL",
|
||||
"ko-KR": "기본 URL",
|
||||
"ja": "ベースURL",
|
||||
"no": "Base URL",
|
||||
@@ -3522,7 +3520,7 @@
|
||||
"en": "API Key",
|
||||
"es": "API Key",
|
||||
"zh-CN": "API密钥",
|
||||
"zh-TW": "API 金鑰",
|
||||
"zh-TW": "API金鑰",
|
||||
"ko-KR": "API 키",
|
||||
"ja": "APIキー",
|
||||
"no": "API-nøkkel",
|
||||
@@ -3537,7 +3535,7 @@
|
||||
"en": "Don't know your API key?",
|
||||
"es": "¿No sabes tu API key?",
|
||||
"zh-CN": "不知道您的API密钥?",
|
||||
"zh-TW": "不知道您的 API 金鑰?",
|
||||
"zh-TW": "不知道您的API金鑰?",
|
||||
"ko-KR": "API 키를 모르시나요?",
|
||||
"ja": "APIキーがわかりませんか?",
|
||||
"no": "Kjenner du ikke API-nøkkelen din?",
|
||||
@@ -3552,7 +3550,7 @@
|
||||
"en": "Click here for instructions",
|
||||
"es": "Clic aquí para instrucciones",
|
||||
"zh-CN": "点击这里查看说明",
|
||||
"zh-TW": "點此檢視說明",
|
||||
"zh-TW": "點擊這裡查看說明",
|
||||
"ko-KR": "설명을 보려면 여기를 클릭하세요",
|
||||
"ja": "説明を見るにはここをクリック",
|
||||
"no": "Klikk her for instruksjoner",
|
||||
@@ -3567,7 +3565,7 @@
|
||||
"en": "Agent",
|
||||
"es": "Agente",
|
||||
"zh-CN": "代理",
|
||||
"zh-TW": "智慧代理",
|
||||
"zh-TW": "代理",
|
||||
"ko-KR": "에이전트",
|
||||
"ja": "エージェント",
|
||||
"no": "Agent",
|
||||
@@ -3642,7 +3640,7 @@
|
||||
"en": "Reset to defaults",
|
||||
"es": "Reiniciar valores por defect",
|
||||
"zh-CN": "重置为默认值",
|
||||
"zh-TW": "還原為預設值",
|
||||
"zh-TW": "重置為預設值",
|
||||
"ko-KR": "기본값으로 재설정",
|
||||
"ja": "デフォルトに戻す",
|
||||
"no": "Tilbakestill til standardverdier",
|
||||
@@ -3746,7 +3744,7 @@
|
||||
"PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECT_TO_GITHUB": {
|
||||
"en": "Connect to GitHub",
|
||||
"zh-CN": "连接到GitHub",
|
||||
"zh-TW": "連線到 GitHub",
|
||||
"zh-TW": "連線到GitHub",
|
||||
"ko-KR": "GitHub에 연결",
|
||||
"ja": "GitHubに接続",
|
||||
"no": "Koble til GitHub",
|
||||
@@ -3792,7 +3790,7 @@
|
||||
"en": "Error authenticating with the LLM provider. Please check your API key",
|
||||
"es": "Error autenticando con el proveedor de LLM. Por favor revisa tu API key",
|
||||
"zh-CN": "LLM认证错误",
|
||||
"zh-TW": "LLM 認證錯誤",
|
||||
"zh-TW": "LLM認證錯誤",
|
||||
"ko-KR": "LLM 인증 오류",
|
||||
"ja": "LLM認証エラー",
|
||||
"no": "Feil ved autentisering med LLM-leverandøren. Vennligst sjekk API-nøkkelen din",
|
||||
@@ -3818,10 +3816,6 @@
|
||||
"es": "Hubo un error al conectar con el entorno de ejecución. Por favor, actualice la página.",
|
||||
"tr": "Çalışma zamanına bağlanırken bir hata oluştu. Lütfen sayfayı yenileyin."
|
||||
},
|
||||
"STATUS$LLM_RETRY": {
|
||||
"en": "Retrying LLM request",
|
||||
"zh-TW": "重新嘗試 LLM 請求中"
|
||||
},
|
||||
"AGENT_ERROR$BAD_ACTION": {
|
||||
"en": "Agent tried to execute a malformed action.",
|
||||
"zh-CN": "错误的操作",
|
||||
@@ -3856,7 +3850,7 @@
|
||||
"en": "Connect to GitHub",
|
||||
"es": "Conectar a GitHub",
|
||||
"zh-CN": "连接到GitHub",
|
||||
"zh-TW": "連線到 GitHub",
|
||||
"zh-TW": "連線到GitHub",
|
||||
"ko-KR": "GitHub에 연결",
|
||||
"ja": "GitHubに接続",
|
||||
"no": "Koble til GitHub",
|
||||
@@ -3871,7 +3865,7 @@
|
||||
"en": "Push to GitHub",
|
||||
"es": "Subir a GitHub",
|
||||
"zh-CN": "推送到GitHub",
|
||||
"zh-TW": "推送到 GitHub",
|
||||
"zh-TW": "推送到GitHub",
|
||||
"ko-KR": "GitHub에 푸시",
|
||||
"ja": "GitHubにプッシュ",
|
||||
"no": "Push til GitHub",
|
||||
@@ -4050,7 +4044,7 @@
|
||||
"ACTION_MESSAGE$RUN_IPYTHON": {
|
||||
"en": "Running a Python command",
|
||||
"zh-CN": "运行IPython",
|
||||
"zh-TW": "執行 IPython",
|
||||
"zh-TW": "執行IPython",
|
||||
"ko-KR": "IPython 실행",
|
||||
"ja": "IPythonを実行",
|
||||
"no": "Kjører en Python-kommando",
|
||||
@@ -4140,7 +4134,7 @@
|
||||
"OBSERVATION_MESSAGE$RUN_IPYTHON": {
|
||||
"en": "Ran a Python command",
|
||||
"zh-CN": "运行IPython",
|
||||
"zh-TW": "執行 IPython",
|
||||
"zh-TW": "執行IPython",
|
||||
"ko-KR": "IPython 실행",
|
||||
"ja": "IPythonを実行",
|
||||
"no": "Kjørte en Python-kommando",
|
||||
@@ -4215,7 +4209,7 @@
|
||||
"EXPANDABLE_MESSAGE$SHOW_DETAILS": {
|
||||
"en": "Show details",
|
||||
"zh-CN": "显示详情",
|
||||
"zh-TW": "顯示詳細資訊",
|
||||
"zh-TW": "顯示詳情",
|
||||
"ko-KR": "상세 정보 표시",
|
||||
"ja": "詳細を表示",
|
||||
"no": "Vis detaljer",
|
||||
@@ -4230,7 +4224,7 @@
|
||||
"EXPANDABLE_MESSAGE$HIDE_DETAILS": {
|
||||
"en": "Hide details",
|
||||
"zh-CN": "隐藏详情",
|
||||
"zh-TW": "隱藏詳細資訊",
|
||||
"zh-TW": "隱藏詳情",
|
||||
"ko-KR": "상세 정보 숨기기",
|
||||
"ja": "詳細を非表示",
|
||||
"no": "Skjul detaljer",
|
||||
@@ -4246,7 +4240,7 @@
|
||||
"en": "AI Provider Configuration",
|
||||
"ja": "AI プロバイダー設定",
|
||||
"zh-CN": "AI 提供商配置",
|
||||
"zh-TW": "AI 供應商設定",
|
||||
"zh-TW": "AI 提供商配置",
|
||||
"ko-KR": "AI 제공자 設정",
|
||||
"no": "AI-leverandørkonfigurasjon",
|
||||
"it": "Configurazione del provider AI",
|
||||
@@ -4321,7 +4315,7 @@
|
||||
"en": "Add best practices docs for contributors",
|
||||
"ja": "コントリビューター向けのベストプラクティスドキュメントを追加",
|
||||
"zh-CN": "为贡献者添加最佳实践文档",
|
||||
"zh-TW": "為貢獻者新增最佳實踐文件",
|
||||
"zh-TW": "為貢獻者添加最佳實踐文檔",
|
||||
"ko-KR": "기여자를 위한 모범 사례 문서 추가",
|
||||
"no": "Legg til beste praksis dokumentasjon for bidragsytere",
|
||||
"it": "Aggiungere documenti sulle migliori pratiche per i contributori",
|
||||
@@ -4336,7 +4330,7 @@
|
||||
"en": "Add/improve a Dockerfile",
|
||||
"ja": "Dockerfileを追加/改善",
|
||||
"zh-CN": "添加/改进 Dockerfile",
|
||||
"zh-TW": "新增/改進 Dockerfile",
|
||||
"zh-TW": "添加/改進 Dockerfile",
|
||||
"ko-KR": "Dockerfile 추가/개선",
|
||||
"no": "Legg til/forbedre en Dockerfile",
|
||||
"it": "Aggiungere/migliorare un Dockerfile",
|
||||
@@ -4366,7 +4360,7 @@
|
||||
"en": "No page loaded.",
|
||||
"ja": "ページが読み込まれていません。",
|
||||
"zh-CN": "页面未加载",
|
||||
"zh-TW": "未載入任何頁面。",
|
||||
"zh-TW": "未加載任何頁面。",
|
||||
"de": "Keine Seite geladen.",
|
||||
"ko-KR": "페이지가 로드되지 않았습니다.",
|
||||
"no": "Ingen side lastet.",
|
||||
@@ -4457,7 +4451,7 @@
|
||||
"en": "Select a GitHub project",
|
||||
"ja": "GitHubプロジェクトを選択",
|
||||
"zh-CN": "选择GitHub项目",
|
||||
"zh-TW": "選擇 GitHub 專案",
|
||||
"zh-TW": "選擇GitHub專案",
|
||||
"ko-KR": "GitHub 프로젝트 선택",
|
||||
"fr": "Sélectionner un projet GitHub",
|
||||
"es": "Seleccionar un proyecto de GitHub",
|
||||
@@ -4472,7 +4466,7 @@
|
||||
"en": "Send",
|
||||
"ja": "送信",
|
||||
"zh-CN": "发送",
|
||||
"zh-TW": "傳送",
|
||||
"zh-TW": "發送",
|
||||
"ko-KR": "보내기",
|
||||
"fr": "Envoyer",
|
||||
"es": "Enviar",
|
||||
@@ -4514,7 +4508,6 @@
|
||||
"de": "Was möchten Sie erstellen?"
|
||||
},
|
||||
"SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL": {
|
||||
"en": "Enable Memory Condenser",
|
||||
"zh-TW": "啟用記憶體壓縮器"
|
||||
"en": "Enable Memory Condenser"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ export const MOCK_DEFAULT_USER_SETTINGS: ApiSettings | PostApiSettings = {
|
||||
DEFAULT_SETTINGS.REMOTE_RUNTIME_RESOURCE_FACTOR,
|
||||
github_token_is_set: DEFAULT_SETTINGS.GITHUB_TOKEN_IS_SET,
|
||||
enable_default_condenser: DEFAULT_SETTINGS.ENABLE_DEFAULT_CONDENSER,
|
||||
user_consents_to_analytics: DEFAULT_SETTINGS.USER_CONSENTS_TO_ANALYTICS,
|
||||
};
|
||||
|
||||
const MOCK_USER_PREFERENCES: {
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
import { QueryClientConfig, QueryCache } from "@tanstack/react-query";
|
||||
import toast from "react-hot-toast";
|
||||
import { retrieveAxiosErrorMessage } from "./utils/retrieve-axios-error-message";
|
||||
import { renderToastIfError } from "./utils/render-toast-if-error";
|
||||
|
||||
const QUERY_KEYS_TO_IGNORE = ["authenticated", "hosts", "settings"];
|
||||
|
||||
const shownErrors = new Set<string>();
|
||||
export const queryClientConfig: QueryClientConfig = {
|
||||
queryCache: new QueryCache({
|
||||
onError: (error, query) => {
|
||||
if (!query.meta?.disableToast) {
|
||||
const errorMessage = retrieveAxiosErrorMessage(error);
|
||||
|
||||
if (!shownErrors.has(errorMessage)) {
|
||||
toast.error(errorMessage || "An error occurred");
|
||||
shownErrors.add(errorMessage);
|
||||
|
||||
setTimeout(() => {
|
||||
shownErrors.delete(errorMessage);
|
||||
}, 3000);
|
||||
}
|
||||
if (!QUERY_KEYS_TO_IGNORE.some((key) => query.queryKey.includes(key))) {
|
||||
renderToastIfError(error);
|
||||
}
|
||||
},
|
||||
}),
|
||||
@@ -27,8 +18,7 @@ export const queryClientConfig: QueryClientConfig = {
|
||||
},
|
||||
mutations: {
|
||||
onError: (error) => {
|
||||
const message = retrieveAxiosErrorMessage(error);
|
||||
toast.error(message);
|
||||
renderToastIfError(error);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -9,7 +9,6 @@ import { WaitlistModal } from "#/components/features/waitlist/waitlist-modal";
|
||||
import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { useAuth } from "#/context/auth-context";
|
||||
import { useMigrateUserConsent } from "#/hooks/use-migrate-user-consent";
|
||||
|
||||
export function ErrorBoundary() {
|
||||
const error = useRouteError();
|
||||
@@ -46,9 +45,10 @@ export function ErrorBoundary() {
|
||||
export default function MainApp() {
|
||||
const { githubTokenIsSet } = useAuth();
|
||||
const { data: settings } = useSettings();
|
||||
const { migrateUserConsent } = useMigrateUserConsent();
|
||||
|
||||
const [consentFormIsOpen, setConsentFormIsOpen] = React.useState(false);
|
||||
const [consentFormIsOpen, setConsentFormIsOpen] = React.useState(
|
||||
!localStorage.getItem("analytics-consent"),
|
||||
);
|
||||
|
||||
const config = useConfig();
|
||||
const {
|
||||
@@ -68,22 +68,6 @@ export default function MainApp() {
|
||||
}
|
||||
}, [settings?.LANGUAGE]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const consentFormModalIsOpen =
|
||||
settings?.USER_CONSENTS_TO_ANALYTICS === null;
|
||||
|
||||
setConsentFormIsOpen(consentFormModalIsOpen);
|
||||
}, [settings]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Migrate user consent to the server if it was previously stored in localStorage
|
||||
migrateUserConsent({
|
||||
handleAnalyticsWasPresentInLocalStorage: () => {
|
||||
setConsentFormIsOpen(false);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const userIsAuthed = !!isAuthed && !authError;
|
||||
const renderWaitlistModal =
|
||||
!isFetchingAuth && !userIsAuthed && config.data?.APP_MODE === "saas";
|
||||
@@ -111,9 +95,7 @@ export default function MainApp() {
|
||||
|
||||
{config.data?.APP_MODE === "oss" && consentFormIsOpen && (
|
||||
<AnalyticsConsentFormModal
|
||||
onClose={() => {
|
||||
setConsentFormIsOpen(false);
|
||||
}}
|
||||
onClose={() => setConsentFormIsOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,6 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: 1,
|
||||
GITHUB_TOKEN_IS_SET: false,
|
||||
ENABLE_DEFAULT_CONDENSER: false,
|
||||
USER_CONSENTS_TO_ANALYTICS: false,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+4
-1
@@ -17,7 +17,10 @@ interface GitHubRepository {
|
||||
id: number;
|
||||
full_name: string;
|
||||
stargazers_count?: number;
|
||||
link_header?: string;
|
||||
}
|
||||
|
||||
interface GitHubAppRepository {
|
||||
repositories: GitHubRepository[];
|
||||
}
|
||||
|
||||
interface GitHubCommit {
|
||||
|
||||
@@ -9,7 +9,6 @@ export type Settings = {
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: number;
|
||||
GITHUB_TOKEN_IS_SET: boolean;
|
||||
ENABLE_DEFAULT_CONDENSER: boolean;
|
||||
USER_CONSENTS_TO_ANALYTICS: boolean | null;
|
||||
};
|
||||
|
||||
export type ApiSettings = {
|
||||
@@ -23,17 +22,14 @@ export type ApiSettings = {
|
||||
remote_runtime_resource_factor: number;
|
||||
github_token_is_set: boolean;
|
||||
enable_default_condenser: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
};
|
||||
|
||||
export type PostSettings = Settings & {
|
||||
github_token: string;
|
||||
unset_github_token: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
};
|
||||
|
||||
export type PostApiSettings = ApiSettings & {
|
||||
github_token: string;
|
||||
unset_github_token: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
};
|
||||
|
||||
+4
-3
@@ -1,11 +1,12 @@
|
||||
import { AxiosError } from "axios";
|
||||
import toast from "react-hot-toast";
|
||||
import { isAxiosErrorWithResponse } from "./type-guards";
|
||||
|
||||
/**
|
||||
* Retrieve the error message from an Axios error
|
||||
* Renders a toast with the error message from an Axios error
|
||||
* @param error The error to render a toast for
|
||||
*/
|
||||
export const retrieveAxiosErrorMessage = (error: AxiosError) => {
|
||||
export const renderToastIfError = (error: AxiosError) => {
|
||||
let errorMessage: string | null = null;
|
||||
|
||||
if (isAxiosErrorWithResponse(error) && error.response?.data.error) {
|
||||
@@ -14,5 +15,5 @@ export const retrieveAxiosErrorMessage = (error: AxiosError) => {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
return errorMessage || "An error occurred";
|
||||
toast.error(errorMessage || "An error occurred");
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
// Here are the list of verified models and providers that we know work well with OpenHands.
|
||||
export const VERIFIED_PROVIDERS = ["openai", "azure", "anthropic", "deepseek"];
|
||||
export const VERIFIED_MODELS = [
|
||||
"o3-mini-2025-01-31",
|
||||
"gpt-4o",
|
||||
"claude-3-5-sonnet-20241022",
|
||||
"deepseek-chat",
|
||||
];
|
||||
@@ -16,8 +16,6 @@ export const VERIFIED_OPENAI_MODELS = [
|
||||
"gpt-4-32k",
|
||||
"o1-mini",
|
||||
"o1-preview",
|
||||
"o3-mini",
|
||||
"o3-mini-2025-01-31",
|
||||
];
|
||||
|
||||
// LiteLLM does not return the compatible Anthropic models with the provider, so we list them here to set them ourselves
|
||||
|
||||
@@ -33,6 +33,7 @@ test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem("FEATURE_MULTI_CONVERSATION_UI", "true");
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ const dirname = path.dirname(filename);
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
});
|
||||
});
|
||||
|
||||
test("should redirect to /conversations after uploading a project zip", async ({
|
||||
|
||||
@@ -2,6 +2,9 @@ import test, { expect, Page } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
});
|
||||
});
|
||||
|
||||
const selectGpt4o = async (page: Page) => {
|
||||
|
||||
@@ -59,6 +59,7 @@ export default defineConfig(({ mode }) => {
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
setupFiles: ["vitest.setup.ts"],
|
||||
reporters: "basic",
|
||||
exclude: [...configDefaults.exclude, "tests"],
|
||||
coverage: {
|
||||
reporter: ["text", "json", "html", "lcov", "text-summary"],
|
||||
|
||||
@@ -11,7 +11,6 @@ from openhands.controller.state.state import State
|
||||
from openhands.core.config import AgentConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.message import ImageContent, Message, TextContent
|
||||
from openhands.core.schema import ActionType
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
AgentDelegateAction,
|
||||
@@ -90,7 +89,15 @@ class CodeActAgent(Agent):
|
||||
self.pending_actions: deque[Action] = deque()
|
||||
self.reset()
|
||||
|
||||
# Retrieve the enabled tools
|
||||
self.mock_function_calling = False
|
||||
if not self.llm.is_function_calling_active():
|
||||
logger.info(
|
||||
f'Function calling not enabled for model {self.llm.config.model}. '
|
||||
'Mocking function calling via prompting.'
|
||||
)
|
||||
self.mock_function_calling = True
|
||||
|
||||
# Function calling mode
|
||||
self.tools = codeact_function_calling.get_tools(
|
||||
codeact_enable_browsing=self.config.codeact_enable_browsing,
|
||||
codeact_enable_jupyter=self.config.codeact_enable_jupyter,
|
||||
@@ -297,27 +304,10 @@ class CodeActAgent(Agent):
|
||||
) # Content is already truncated by openhands-aci
|
||||
elif isinstance(obs, BrowserOutputObservation):
|
||||
text = obs.get_agent_obs_text()
|
||||
if (
|
||||
obs.trigger_by_action == ActionType.BROWSE_INTERACTIVE
|
||||
and obs.set_of_marks is not None
|
||||
and len(obs.set_of_marks) > 0
|
||||
and self.config.enable_som_visual_browsing
|
||||
and self.llm.vision_is_active()
|
||||
and self.llm.is_visual_browser_tool_supported()
|
||||
):
|
||||
text += 'Image: Current webpage screenshot (Note that only visible portion of webpage is present in the screenshot. You may need to scroll to view the remaining portion of the web-page.)\n'
|
||||
message = Message(
|
||||
role='user',
|
||||
content=[
|
||||
TextContent(text=text),
|
||||
ImageContent(image_urls=[obs.set_of_marks]),
|
||||
],
|
||||
)
|
||||
else:
|
||||
message = Message(
|
||||
role='user',
|
||||
content=[TextContent(text=text)],
|
||||
)
|
||||
message = Message(
|
||||
role='user',
|
||||
content=[TextContent(text=text)],
|
||||
)
|
||||
elif isinstance(obs, AgentDelegateObservation):
|
||||
text = truncate_content(
|
||||
obs.outputs['content'] if 'content' in obs.outputs else '',
|
||||
@@ -389,6 +379,8 @@ class CodeActAgent(Agent):
|
||||
'messages': self.llm.format_messages_for_llm(messages),
|
||||
}
|
||||
params['tools'] = self.tools
|
||||
if self.mock_function_calling:
|
||||
params['mock_function_calling'] = True
|
||||
response = self.llm.completion(**params)
|
||||
actions = codeact_function_calling.response_to_actions(response)
|
||||
for action in actions:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user