Compare commits
8 Commits
feature/pr
...
fix-cli-te
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d295552a2f | ||
|
|
17d47e963a | ||
|
|
0e22b3f8dd | ||
|
|
b65bc3c5ed | ||
|
|
98ad453493 | ||
|
|
bdec589ea3 | ||
|
|
cf67b98387 | ||
|
|
a6833d8d32 |
@@ -12,8 +12,5 @@
|
||||
"ghcr.io/devcontainers/features/node:1": {},
|
||||
},
|
||||
"postCreateCommand": ".devcontainer/setup.sh",
|
||||
"runArgs": ["--add-host=host.docker.internal:host-gateway"],
|
||||
"containerEnv": {
|
||||
"DOCKER_HOST_ADDR": "host.docker.internal"
|
||||
},
|
||||
"runArgs": ["--network=host"],
|
||||
}
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
# NodeJS
|
||||
frontend/node_modules
|
||||
|
||||
# Configuration (except pyproject.toml)
|
||||
*.ini
|
||||
*.toml
|
||||
!pyproject.toml
|
||||
*.yml
|
||||
|
||||
# Documentation (except README.md)
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# Hidden files and directories
|
||||
.*
|
||||
__pycache__
|
||||
|
||||
# Unneded files and directories
|
||||
/dev_config/
|
||||
/docs/
|
||||
/evaluation/
|
||||
/tests/
|
||||
CITATION.cff
|
||||
config.toml
|
||||
.envrc
|
||||
.env
|
||||
.git
|
||||
|
||||
6
.github/dependabot.yml
vendored
@@ -72,9 +72,3 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directories:
|
||||
- "containers/*"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
@@ -44,13 +44,7 @@ Frontend:
|
||||
- Available variables: VITE_BACKEND_HOST, VITE_USE_TLS, VITE_INSECURE_SKIP_VERIFY, VITE_FRONTEND_PORT
|
||||
- Internationalization:
|
||||
- Generate i18n declaration file: `npm run make-i18n`
|
||||
- Data Fetching & Cache Management:
|
||||
- We use TanStack Query (fka React Query) for data fetching and cache management
|
||||
- Data Access Layer: API client methods are located in `frontend/src/api` and should never be called directly from UI components - they must always be wrapped with TanStack Query
|
||||
- Custom hooks are located in `frontend/src/hooks/query/` and `frontend/src/hooks/mutation/`
|
||||
- Query hooks should follow the pattern use[Resource] (e.g., `useConversationMicroagents`)
|
||||
- Mutation hooks should follow the pattern use[Action] (e.g., `useDeleteConversation`)
|
||||
- Architecture rule: UI components → TanStack Query hooks → Data Access Layer (`frontend/src/api`) → API endpoints
|
||||
|
||||
|
||||
## Template for Github Pull Request
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ poetry run pytest ./tests/unit/test_*.py
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
|
||||
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.43-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.41-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
17
README.md
@@ -18,17 +18,6 @@
|
||||
<a href="https://docs.all-hands.dev/usage/getting-started"><img src="https://img.shields.io/badge/Documentation-000?logo=googledocs&logoColor=FFE165&style=for-the-badge" alt="Check out the documentation"></a>
|
||||
<a href="https://arxiv.org/abs/2407.16741"><img src="https://img.shields.io/badge/Paper%20on%20Arxiv-000?logoColor=FFE165&logo=arxiv&style=for-the-badge" alt="Paper on Arxiv"></a>
|
||||
<a href="https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0#gid=0"><img src="https://img.shields.io/badge/Benchmark%20score-000?logoColor=FFE165&logo=huggingface&style=for-the-badge" alt="Evaluation Benchmark Score"></a>
|
||||
|
||||
<!-- Keep these links. Translations will automatically update with the README. -->
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=de">Deutsch</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=es">Español</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=fr">français</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=ja">日本語</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=ko">한국어</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=pt">Português</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=ru">Русский</a> |
|
||||
<a href="https://www.readme-i18n.com/All-Hands-AI/OpenHands?lang=zh">中文</a>
|
||||
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
@@ -62,17 +51,17 @@ system requirements and more information.
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.43
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
```
|
||||
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||
|
||||
@@ -51,17 +51,17 @@ OpenHands也可以使用Docker在本地系统上运行。
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.43
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
```
|
||||
|
||||
您将在[http://localhost:3000](http://localhost:3000)找到运行中的OpenHands!
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
ARG OPENHANDS_BUILD_VERSION=dev
|
||||
FROM node:22.16.0-bookworm-slim AS frontend-builder
|
||||
FROM node:21.7.2-bookworm-slim AS frontend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY frontend/package.json frontend/package-lock.json ./
|
||||
COPY ./frontend/package.json frontend/package-lock.json ./
|
||||
RUN npm install -g npm@10.5.1
|
||||
RUN npm ci
|
||||
|
||||
COPY frontend ./
|
||||
COPY ./frontend ./
|
||||
RUN npm run build
|
||||
|
||||
FROM python:3.12.10-slim AS base
|
||||
FROM base AS backend-builder
|
||||
FROM python:3.12.3-slim AS backend-builder
|
||||
|
||||
WORKDIR /app
|
||||
ENV PYTHONPATH='/app'
|
||||
@@ -22,18 +22,17 @@ ENV POETRY_NO_INTERACTION=1 \
|
||||
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y curl make git build-essential \
|
||||
&& python3 -m pip install poetry --break-system-packages
|
||||
&& python3 -m pip install poetry==1.8.2 --break-system-packages
|
||||
|
||||
COPY pyproject.toml poetry.lock ./
|
||||
COPY ./pyproject.toml ./poetry.lock ./
|
||||
RUN touch README.md
|
||||
RUN export POETRY_CACHE_DIR && poetry install --no-root && rm -rf $POETRY_CACHE_DIR
|
||||
|
||||
FROM base AS openhands-app
|
||||
FROM python:3.12.3-slim AS openhands-app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# re-declare for this section
|
||||
ARG OPENHANDS_BUILD_VERSION
|
||||
ARG OPENHANDS_BUILD_VERSION #re-declare for this section
|
||||
|
||||
ENV RUN_AS_OPENHANDS=true
|
||||
# A random number--we need this to be different from the user's UID on the host machine
|
||||
@@ -75,7 +74,12 @@ COPY --chown=openhands:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${V
|
||||
COPY --chown=openhands:app --chmod=770 ./microagents ./microagents
|
||||
COPY --chown=openhands:app --chmod=770 ./openhands ./openhands
|
||||
COPY --chown=openhands:app --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
|
||||
COPY --chown=openhands:app pyproject.toml poetry.lock README.md MANIFEST.in LICENSE ./
|
||||
COPY --chown=openhands:app --chmod=770 ./openhands/agenthub ./openhands/agenthub
|
||||
COPY --chown=openhands:app ./pyproject.toml ./pyproject.toml
|
||||
COPY --chown=openhands:app ./poetry.lock ./poetry.lock
|
||||
COPY --chown=openhands:app ./README.md ./README.md
|
||||
COPY --chown=openhands:app ./MANIFEST.in ./MANIFEST.in
|
||||
COPY --chown=openhands:app ./LICENSE ./LICENSE
|
||||
|
||||
# This is run as "openhands" user, and will create __pycache__ with openhands:openhands ownership
|
||||
RUN python openhands/core/download.py # No-op to download assets
|
||||
|
||||
@@ -10,9 +10,8 @@ services:
|
||||
environment:
|
||||
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
- DOCKER_HOST_ADDR=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.43-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.41-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -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.43-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of openhands-state for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Setup
|
||||
|
||||
```
|
||||
npm install -g mint
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
yarn global add mint
|
||||
```
|
||||
|
||||
# Preview
|
||||
|
||||
```
|
||||
mint dev
|
||||
```
|
||||
104
docs/docs.json
@@ -34,8 +34,7 @@
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"usage/cloud/github-installation",
|
||||
"usage/cloud/gitlab-installation",
|
||||
"usage/cloud/slack-installation"
|
||||
"usage/cloud/gitlab-installation"
|
||||
]
|
||||
},
|
||||
"usage/cloud/cloud-ui",
|
||||
@@ -49,62 +48,13 @@
|
||||
"usage/how-to/gui-mode",
|
||||
"usage/how-to/cli-mode",
|
||||
"usage/how-to/headless-mode",
|
||||
"usage/how-to/github-action",
|
||||
{
|
||||
"group": "Advanced Configuration",
|
||||
"pages": [
|
||||
{
|
||||
"group": "LLM Configuration",
|
||||
"pages": [
|
||||
"usage/llms/llms",
|
||||
{
|
||||
"group": "Providers",
|
||||
"pages": [
|
||||
"usage/llms/azure-llms",
|
||||
"usage/llms/google-llms",
|
||||
"usage/llms/groq",
|
||||
"usage/llms/local-llms",
|
||||
"usage/llms/litellm-proxy",
|
||||
"usage/llms/openai-llms",
|
||||
"usage/llms/openrouter"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Runtime Configuration",
|
||||
"pages": [
|
||||
"usage/runtimes/overview",
|
||||
{
|
||||
"group": "Providers",
|
||||
"pages": [
|
||||
"usage/runtimes/docker",
|
||||
"usage/runtimes/remote",
|
||||
"usage/runtimes/local",
|
||||
{
|
||||
"group": "Third-Party Providers",
|
||||
"pages": [
|
||||
"usage/runtimes/modal",
|
||||
"usage/runtimes/daytona",
|
||||
"usage/runtimes/runloop",
|
||||
"usage/runtimes/e2b"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"usage/configuration-options",
|
||||
"usage/how-to/custom-sandbox-guide",
|
||||
"usage/search-engine-setup",
|
||||
"usage/mcp"
|
||||
]
|
||||
}
|
||||
"usage/how-to/github-action"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Customization",
|
||||
"pages": [
|
||||
"usage/prompting/prompting-best-practices",
|
||||
"usage/prompting/repository",
|
||||
{
|
||||
"group": "Microagents",
|
||||
@@ -119,9 +69,53 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Tips and Tricks",
|
||||
"group": "Advanced Configuration",
|
||||
"pages": [
|
||||
"usage/prompting/prompting-best-practices"
|
||||
{
|
||||
"group": "LLM Configuration",
|
||||
"pages": [
|
||||
"usage/llms/llms",
|
||||
{
|
||||
"group": "Providers",
|
||||
"pages": [
|
||||
"usage/llms/azure-llms",
|
||||
"usage/llms/google-llms",
|
||||
"usage/llms/groq",
|
||||
"usage/llms/local-llms",
|
||||
"usage/llms/litellm-proxy",
|
||||
"usage/llms/openai-llms",
|
||||
"usage/llms/openrouter"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Runtime Configuration",
|
||||
"pages": [
|
||||
"usage/runtimes/overview",
|
||||
{
|
||||
"group": "Providers",
|
||||
"pages": [
|
||||
"usage/runtimes/docker",
|
||||
"usage/runtimes/remote",
|
||||
"usage/runtimes/local",
|
||||
{
|
||||
"group": "Third-Party Providers",
|
||||
"pages": [
|
||||
"usage/runtimes/modal",
|
||||
"usage/runtimes/daytona",
|
||||
"usage/runtimes/runloop",
|
||||
"usage/runtimes/e2b"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"usage/configuration-options",
|
||||
"usage/how-to/custom-sandbox-guide",
|
||||
"usage/search-engine-setup",
|
||||
"usage/mcp"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
BIN
docs/static/img/api-key-generation.png
vendored
|
Before Width: | Height: | Size: 18 KiB |
BIN
docs/static/img/docs/api-key-generation.png
vendored
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/static/img/slack-create-convo.png
vendored
|
Before Width: | Height: | Size: 113 KiB |
BIN
docs/static/img/slack-pro-tip.png
vendored
|
Before Width: | Height: | Size: 118 KiB |
BIN
docs/static/img/slack-results-and-follow-up.png
vendored
|
Before Width: | Height: | Size: 542 KiB |
@@ -1,11 +1,9 @@
|
||||
---
|
||||
title: Cloud API
|
||||
description: OpenHands Cloud provides a REST API that allows you to programmatically interact with OpenHands.
|
||||
This guide explains how to obtain an API key and use the API to start conversations and retrieve their status.
|
||||
description: OpenHands Cloud provides a REST API that allows you to programmatically interact with the service. This guide explains how to obtain an API key and use the API to start conversations.
|
||||
---
|
||||
|
||||
For the available API endpoints, refer to the
|
||||
[OpenHands API Reference](https://docs.all-hands.dev/api-reference).
|
||||
For more detailed information about the API, refer to the [OpenHands API Reference](https://docs.all-hands.dev/swagger-ui/).
|
||||
|
||||
## Obtaining an API Key
|
||||
|
||||
@@ -18,7 +16,7 @@ To use the OpenHands Cloud API, you'll need to generate an API key:
|
||||
5. Give your key a descriptive name (Example: "Development" or "Production") and select `Create`.
|
||||
6. Copy the generated API key and store it securely. It will only be shown once.
|
||||
|
||||

|
||||

|
||||
|
||||
## API Usage
|
||||
|
||||
@@ -35,81 +33,87 @@ To start a new conversation with OpenHands to perform a task, you'll need to mak
|
||||
|
||||
#### Examples
|
||||
|
||||
<details>
|
||||
<summary>cURL</summary>
|
||||
|
||||
<Accordion title="cURL">
|
||||
```bash
|
||||
curl -X POST "https://app.all-hands.dev/api/conversations" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"initial_user_msg": "Check whether there is any incorrect information in the README.md file and send a PR to fix it if so.",
|
||||
"repository": "yourusername/your-repo"
|
||||
}'
|
||||
```
|
||||
</Accordion>
|
||||
```bash
|
||||
curl -X POST "https://app.all-hands.dev/api/conversations" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"initial_user_msg": "Check whether there is any incorrect information in the README.md file and send a PR to fix it if so.",
|
||||
"repository": "yourusername/your-repo"
|
||||
}'
|
||||
```
|
||||
</details>
|
||||
|
||||
<Accordion title="Python (with requests)">
|
||||
```python
|
||||
import requests
|
||||
<details>
|
||||
<summary>Python (with requests)</summary>
|
||||
|
||||
api_key = "YOUR_API_KEY"
|
||||
url = "https://app.all-hands.dev/api/conversations"
|
||||
```python
|
||||
import requests
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
api_key = "YOUR_API_KEY"
|
||||
url = "https://app.all-hands.dev/api/conversations"
|
||||
|
||||
data = {
|
||||
"initial_user_msg": "Check whether there is any incorrect information in the README.md file and send a PR to fix it if so.",
|
||||
"repository": "yourusername/your-repo"
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
conversation = response.json()
|
||||
|
||||
print(f"Conversation Link: https://app.all-hands.dev/conversations/{conversation['conversation_id']}")
|
||||
print(f"Status: {conversation['status']}")
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="TypeScript/JavaScript (with fetch)">
|
||||
```typescript
|
||||
const apiKey = "YOUR_API_KEY";
|
||||
const url = "https://app.all-hands.dev/api/conversations";
|
||||
|
||||
const headers = {
|
||||
"Authorization": `Bearer ${apiKey}`,
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
}
|
||||
|
||||
const data = {
|
||||
initial_user_msg: "Check whether there is any incorrect information in the README.md file and send a PR to fix it if so.",
|
||||
repository: "yourusername/your-repo"
|
||||
};
|
||||
data = {
|
||||
"initial_user_msg": "Check whether there is any incorrect information in the README.md file and send a PR to fix it if so.",
|
||||
"repository": "yourusername/your-repo"
|
||||
}
|
||||
|
||||
async function startConversation() {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
conversation = response.json()
|
||||
|
||||
const conversation = await response.json();
|
||||
print(f"Conversation Link: https://app.all-hands.dev/conversations/{conversation['conversation_id']}")
|
||||
print(f"Status: {conversation['status']}")
|
||||
```
|
||||
</details>
|
||||
|
||||
console.log(`Conversation Link: https://app.all-hands.dev/conversations/${conversation.id}`);
|
||||
console.log(`Status: ${conversation.status}`);
|
||||
<details>
|
||||
<summary>TypeScript/JavaScript (with fetch)</summary>
|
||||
|
||||
return conversation;
|
||||
} catch (error) {
|
||||
console.error("Error starting conversation:", error);
|
||||
}
|
||||
```typescript
|
||||
const apiKey = "YOUR_API_KEY";
|
||||
const url = "https://app.all-hands.dev/api/conversations";
|
||||
|
||||
const headers = {
|
||||
"Authorization": `Bearer ${apiKey}`,
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
|
||||
const data = {
|
||||
initial_user_msg: "Check whether there is any incorrect information in the README.md file and send a PR to fix it if so.",
|
||||
repository: "yourusername/your-repo"
|
||||
};
|
||||
|
||||
async function startConversation() {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const conversation = await response.json();
|
||||
|
||||
console.log(`Conversation Link: https://app.all-hands.dev/conversations/${conversation.id}`);
|
||||
console.log(`Status: ${conversation.status}`);
|
||||
|
||||
return conversation;
|
||||
} catch (error) {
|
||||
console.error("Error starting conversation:", error);
|
||||
}
|
||||
}
|
||||
|
||||
startConversation();
|
||||
```
|
||||
</Accordion>
|
||||
startConversation();
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Response
|
||||
|
||||
@@ -141,12 +145,14 @@ GET https://app.all-hands.dev/api/conversations/{conversation_id}
|
||||
|
||||
#### Example
|
||||
|
||||
<Accordion title="cURL">
|
||||
```bash
|
||||
curl -X GET "https://app.all-hands.dev/api/conversations/{conversation_id}" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY"
|
||||
```
|
||||
</Accordion>
|
||||
<details>
|
||||
<summary>cURL</summary>
|
||||
|
||||
```bash
|
||||
curl -X GET "https://app.all-hands.dev/api/conversations/{conversation_id}" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY"
|
||||
```
|
||||
</details>
|
||||
|
||||
#### Response
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ The Settings page allows you to:
|
||||
|
||||
## Key Features
|
||||
|
||||
For an overview of the key features available inside a conversation, please refer to the [Key Features](/usage/key-features)
|
||||
For an overview of the key features available inside a conversation, please refer to the [Key Features](../key-features)
|
||||
section of the documentation.
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -19,12 +19,6 @@ appropriate repository and branch you'd like OpenHands to work on. Then click on
|
||||
|
||||

|
||||
|
||||
## Using Tokens with Reduced Scopes
|
||||
|
||||
OpenHands requests an API-scoped token during OAuth authentication. By default, this token is provided to the agent.
|
||||
To restrict the agent's permissions, you can define a custom secret `GITLAB_TOKEN`, which will override the default token assigned to the agent.
|
||||
While the high-permission API token is still requested and used for other components of the application (e.g. opening merge requests), the agent will not have access to it.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Learn about the Cloud UI](/usage/cloud/cloud-ui).
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
title: Slack Integration - Coming soon...
|
||||
description: This guide walks you through installing the OpenHands Slack app.
|
||||
---
|
||||
|
||||
<Warning>This integration is not live yet, but will be available soon.</Warning>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- You are a slack workspace admin
|
||||
- Access to OpenHands Cloud
|
||||
|
||||
## Installation Steps
|
||||
|
||||
1. Log in to [OpenHands Cloud](https://app.all-hands.dev)
|
||||
2. Click the button below to OpenHands Slack App <a target="_blank" href="https://slack.com/oauth/v2/authorize?client_id=7477886716822.8729519890534&scope=app_mentions:read,chat:write,users:read,channels:history,groups:history,mpim:history,im:history&user_scope=channels:history,groups:history,im:history,mpim:history"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcSet="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a>
|
||||
3. In the top right corner, select the workspace to install the OpenHands Slack app.
|
||||
4. Review permissions and click allow
|
||||
|
||||
## Working With the Slack App
|
||||
|
||||
To start a new conversation, you can mention `@openhands` in a new message or a thread inside any Slack channel.
|
||||
|
||||
Once a conversation is started, all thread messages underneath it will be follow-up messages to OpenHands.
|
||||
|
||||
To send follow-up messages for the same conversation, mention `@openhands` in a thread reply to the original message. You must be the user who started the conversation.
|
||||
|
||||
## Example conversation
|
||||
|
||||
### Start a new conversation, and select repo
|
||||
|
||||
Conversation is started by mentioning `@openhands`.
|
||||
|
||||

|
||||
|
||||
### See agent response and send follow up messages
|
||||
|
||||
Initial request is followed up by mentioning `@openhands` in a thread reply.
|
||||
|
||||

|
||||
|
||||
## Pro tip
|
||||
|
||||
You can mention a repo name when starting a new conversation in the following formats
|
||||
|
||||
1. "My-Repo" repo (e.g `@openhands in the openhands repo ...`)
|
||||
2. "All-Hands-AI/OpenHands" (e.g `@openhands in All-Hands-AI/OpenHands ...`)
|
||||
|
||||
The repo match is case insensitive. If a repo name match is made, it will kick off the conversation.
|
||||
If the repo name partially matches against, multiple repos, you'll be asked to select a repo from the filtered list.
|
||||
|
||||

|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
title: CLI
|
||||
description: The Command-Line Interface (CLI) provides a powerful interface that lets you engage with OpenHands
|
||||
directly from your terminal.
|
||||
title: CLI Mode
|
||||
description: CLI mode provides a powerful interactive Command-Line Interface (CLI) that lets you engage with OpenHands directly from your terminal.
|
||||
---
|
||||
|
||||
This mode is different from the [headless mode](/usage/how-to/headless-mode), which is non-interactive and better
|
||||
for scripting.
|
||||
This mode is different from the [headless mode](./headless-mode), which is non-interactive and better for scripting.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -46,7 +44,7 @@ poetry run python -m openhands.cli.main
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -55,7 +53,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.43 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41 \
|
||||
python -m openhands.cli.main --override-cli-mode true
|
||||
```
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
---
|
||||
title: GUI
|
||||
description: High level overview of the Graphical User Interface (GUI) in OpenHands.
|
||||
title: GUI Mode
|
||||
description: OpenHands provides a Graphical User Interface (GUI) mode for interacting with the AI assistant.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
## Installation and Setup
|
||||
|
||||
- [OpenHands is running](/usage/local-setup)
|
||||
1. Follow the installation instructions to install OpenHands.
|
||||
2. After running the command, access OpenHands at [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
## Overview
|
||||
## Interacting with the GUI
|
||||
|
||||
### Initial Setup
|
||||
|
||||
@@ -18,23 +19,16 @@ description: High level overview of the Graphical User Interface (GUI) in OpenHa
|
||||
3. Enter the corresponding `API Key` for your chosen provider.
|
||||
4. Click `Save Changes` to apply the settings.
|
||||
|
||||
### Settings
|
||||
### Version Control Tokens
|
||||
|
||||
You can use the Settings page at any time to:
|
||||
OpenHands supports multiple version control providers. You can configure tokens for multiple providers simultaneously.
|
||||
|
||||
- Setup the LLM provider and model for OpenHands.
|
||||
- [Setup the search engine](/usage/search-engine-setup).
|
||||
- [Configure MCP servers](/usage/mcp).
|
||||
- [Connect to GitHub](/usage/how-to/gui-mode#github-setup) and [connect to GitLab](/usage/how-to/gui-mode#gitlab-setup)
|
||||
- Set application settings like your preferred language, notifications and other preferences.
|
||||
- Generate custom secrets.
|
||||
|
||||
#### GitHub Setup
|
||||
#### GitHub Token Setup
|
||||
|
||||
OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if provided:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Setting Up a GitHub Token">
|
||||
<details>
|
||||
<summary>Setting Up a GitHub Token</summary>
|
||||
|
||||
1. **Generate a Personal Access Token (PAT)**:
|
||||
- On GitHub, go to Settings > Developer Settings > Personal Access Tokens > Tokens (classic).
|
||||
@@ -43,11 +37,16 @@ OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if pro
|
||||
- `repo` (Full control of private repositories)
|
||||
- **Fine-Grained Tokens**
|
||||
- All Repositories (You can select specific repositories, but this will impact what returns in repo search)
|
||||
- Minimal Permissions (Select `Meta Data = Read-only` read for search, `Pull Requests = Read and Write` and `Content = Read and Write` for branch creation)
|
||||
- Minimal Permissions ( Select `Meta Data = Read-only` read for search, `Pull Requests = Read and Write` and `Content = Read and Write` for branch creation)
|
||||
2. **Enter Token in OpenHands**:
|
||||
- In the Settings page, navigate to the `Git` tab.
|
||||
- Click the Settings button (gear icon).
|
||||
- Navigate to the `Git` tab.
|
||||
- Paste your token in the `GitHub Token` field.
|
||||
- Click `Save Changes` to apply the changes.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Organizational Token Policies</summary>
|
||||
|
||||
If you're working with organizational repositories, additional setup may be required:
|
||||
|
||||
@@ -60,12 +59,15 @@ OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if pro
|
||||
- Look for the organization under `Organization access`.
|
||||
- If required, click `Enable SSO` next to your organization.
|
||||
- Complete the SSO authorization process.
|
||||
</Accordion>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Troubleshooting</summary>
|
||||
|
||||
<Accordion title="Troubleshooting">
|
||||
Common issues and solutions:
|
||||
|
||||
- **Token Not Recognized**:
|
||||
- Ensure the token is properly saved in settings.
|
||||
- Check that the token hasn't expired.
|
||||
- Verify the token has the required scopes.
|
||||
- Try regenerating the token.
|
||||
@@ -79,15 +81,15 @@ OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if pro
|
||||
- The app will show a green checkmark if the token is valid.
|
||||
- Try accessing a repository to confirm permissions.
|
||||
- Check the browser console for any error messages.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</details>
|
||||
|
||||
#### GitLab Setup
|
||||
#### GitLab Token Setup
|
||||
|
||||
OpenHands automatically exports a `GITLAB_TOKEN` to the shell environment if provided:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Setting Up a GitLab Token">
|
||||
<details>
|
||||
<summary>Setting Up a GitLab Token</summary>
|
||||
|
||||
1. **Generate a Personal Access Token (PAT)**:
|
||||
- On GitLab, go to User Settings > Access Tokens.
|
||||
- Create a new token with the following scopes:
|
||||
@@ -97,17 +99,15 @@ OpenHands automatically exports a `GITLAB_TOKEN` to the shell environment if pro
|
||||
- `write_repository` (Write repository)
|
||||
- Set an expiration date or leave it blank for a non-expiring token.
|
||||
2. **Enter Token in OpenHands**:
|
||||
- In the Settings page, navigate to the `Git` tab.
|
||||
- Click the Settings button (gear icon).
|
||||
- Navigate to the `Git` tab.
|
||||
- Paste your token in the `GitLab Token` field.
|
||||
- Click `Save Changes` to apply the changes.
|
||||
</details>
|
||||
|
||||
3. **(Optional): Restrict agent permissions**
|
||||
- Create another PAT using Step 1 and exclude `api` scope .
|
||||
- In the Settings page, in the `Secrets` tab, create a new secret `GITLAB_TOKEN` and paste your lower scope token.
|
||||
- OpenHands will use the higher scope token, and the agent will use the lower scope token
|
||||
</Accordion>
|
||||
<details>
|
||||
<summary>Troubleshooting</summary>
|
||||
|
||||
<Accordion title="Troubleshooting">
|
||||
Common issues and solutions:
|
||||
|
||||
- **Token Not Recognized**:
|
||||
@@ -119,44 +119,25 @@ OpenHands automatically exports a `GITLAB_TOKEN` to the shell environment if pro
|
||||
- Verify project access permissions.
|
||||
- Check if the token has the necessary scopes.
|
||||
- For group/organization repositories, ensure you have proper access.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</details>
|
||||
|
||||
#### Advanced Settings
|
||||
### Advanced Settings
|
||||
|
||||
The `Advanced` settings allows configuration of additional LLM settings. Inside the Settings page, under the `LLM` tab,
|
||||
toggle `Advanced` options to access additional settings.
|
||||
1. Inside the Settings page, under the `LLM` tab, toggle `Advanced` options to access additional settings.
|
||||
2. Use the `Custom Model` text box to manually enter a model if it's not in the list.
|
||||
3. Specify a `Base URL` if required by your LLM provider.
|
||||
|
||||
- Custom Model: Use the `Custom Model` text box to manually enter a model. Make sure to use the correct prefix based on litellm docs.
|
||||
- Base URL: Specify a `Base URL` if required by your LLM provider.
|
||||
- Memory Condensation: The memory condenser manages the LLM's context by ensuring only the most important and relevant information is presented.
|
||||
- Confirmation Mode: Enabling this mode will cause OpenHands to confirm an action with the user before performing it.
|
||||
### Interacting with the AI
|
||||
|
||||
### Key Features
|
||||
|
||||
For an overview of the key features available inside a conversation, please refer to the [Key Features](/usage/key-features)
|
||||
section of the documentation.
|
||||
|
||||
### Status Indicator
|
||||
|
||||
The status indicator located in the bottom left of the screen will cycle through a number of states as a new conversation
|
||||
is loaded. Typically these include:
|
||||
|
||||
* `Disconnected` : The frontend is not connected to any conversation
|
||||
* `Connecting` : The frontend is connecting a websocket to a conversation.
|
||||
* `Building Runtime...` : The server is building a runtime. This is typically in development mode only while building a docker image.
|
||||
* `Starting Runtime...` : The server is starting a new runtime instance - probably a new docker container or remote runtime.
|
||||
* `Initializing Agent...` : The server is starting the agent loop. (This step does not appear at present with Nested runtimes)
|
||||
* `Setting up workspace...` : Usually this means a `git clone ...` operation.
|
||||
* `Setting up git hooks` : Setting up the git pre commit hooks for the workspace.
|
||||
* `Agent is awaiting user input...` : Ready to go!
|
||||
1. Type your prompt in the input box.
|
||||
2. Click the send button or press Enter to submit your message.
|
||||
3. The AI will process your input and provide a response in the chat window.
|
||||
4. You can continue the conversation by asking follow-up questions or providing additional information.
|
||||
|
||||
## Tips for Effective Use
|
||||
|
||||
- Be specific in your requests to get the most accurate and helpful responses, as described in the [prompting best practices](../prompting/prompting-best-practices).
|
||||
- Use one of the recommended models, as described in the [LLMs section](usage/llms/llms.md).
|
||||
|
||||
## Other Ways to Run Openhands
|
||||
- [Run OpenHands in a scriptable headless mode.](/usage/how-to/headless-mode)
|
||||
- [Run OpenHands with a friendly CLI.](/usage/how-to/cli-mode)
|
||||
- [Run OpenHands on GitHub issues with a GitHub action.](/usage/how-to/github-action)
|
||||
Remember, the GUI mode of OpenHands is designed to make your interaction with the AI assistant as smooth and intuitive
|
||||
as possible. Don't hesitate to explore its features to maximize your productivity.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: Headless
|
||||
description: You can run OpenHands with a single command, without starting the web application. This makes it easy to
|
||||
write scripts and automate tasks with OpenHands.
|
||||
title: Headless Mode
|
||||
description: You can run OpenHands with a single command, without starting the web application. This makes it easy to write scripts and automate tasks with OpenHands.
|
||||
---
|
||||
|
||||
This is different from [the CLI](./cli-mode), which is interactive, and better for active development.
|
||||
This is different from [CLI Mode](./cli-mode), which is interactive, and better for active development.
|
||||
|
||||
## With Python
|
||||
|
||||
@@ -32,7 +31,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.43-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -42,7 +41,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.43 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ To get started with OpenHands Cloud, visit [app.all-hands.dev](https://app.all-h
|
||||
|
||||
For more information see [getting started with OpenHands Cloud.](/usage/cloud/openhands-cloud)
|
||||
|
||||
## Running OpenHands on Your Own
|
||||
## Running OpenHands Locally
|
||||
|
||||
Run OpenHands on your local system and bring your own LLM and API key.
|
||||
|
||||
|
||||
@@ -14,28 +14,23 @@ recommendations for model selection. Our latest benchmarking results can be foun
|
||||
|
||||
Based on these findings and community feedback, these are the latest models that have been verified to work reasonably well with OpenHands:
|
||||
|
||||
### Cloud / API-Based Models
|
||||
|
||||
- [anthropic/claude-sonnet-4-20250514](https://www.anthropic.com/api) (recommended)
|
||||
- [openai/o4-mini](https://openai.com/index/introducing-o3-and-o4-mini/)
|
||||
- [gemini/gemini-2.5-pro](https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/)
|
||||
- [deepseek/deepseek-chat](https://api-docs.deepseek.com/)
|
||||
- [all-hands/openhands-lm-32b-v0.1](https://www.all-hands.dev/blog/introducing-openhands-lm-32b----a-strong-open-coding-agent-model) -- available through [OpenRouter](https://openrouter.ai/all-hands/openhands-lm-32b-v0.1)
|
||||
|
||||
If you have successfully run OpenHands with specific providers, we encourage you to open a PR to share your setup process
|
||||
to help others using the same provider!
|
||||
|
||||
For a full list of the providers and models available, please consult the
|
||||
[litellm documentation](https://docs.litellm.ai/docs/providers).
|
||||
|
||||
<Warning>
|
||||
OpenHands will issue many prompts to the LLM you configure. Most of these LLMs cost money, so be sure to set spending
|
||||
limits and monitor usage.
|
||||
</Warning>
|
||||
|
||||
### Local / Self-Hosted Models
|
||||
If you have successfully run OpenHands with specific providers, we encourage you to open a PR to share your setup process
|
||||
to help others using the same provider!
|
||||
|
||||
- [mistralai/devstral-small](https://www.all-hands.dev/blog/devstral-a-new-state-of-the-art-open-model-for-coding-agents) (20 May 2025) -- also available through [OpenRouter](https://openrouter.ai/mistralai/devstral-small:free)
|
||||
- [all-hands/openhands-lm-32b-v0.1](https://www.all-hands.dev/blog/introducing-openhands-lm-32b----a-strong-open-coding-agent-model) (31 March 2025) -- also available through [OpenRouter](https://openrouter.ai/all-hands/openhands-lm-32b-v0.1)
|
||||
For a full list of the providers and models available, please consult the
|
||||
[litellm documentation](https://docs.litellm.ai/docs/providers).
|
||||
|
||||
<Note>
|
||||
Most current local and open source models are not as powerful. When using such models, you may see long
|
||||
|
||||
@@ -54,25 +54,25 @@ Check [the installation guide](/usage/local-setup) to make sure you have all the
|
||||
export LMSTUDIO_MODEL_NAME="imported-models/uncategorized/devstralq4_k_m.gguf" # <- Replace this with the model name you copied from LMStudio
|
||||
export LMSTUDIO_URL="http://host.docker.internal:1234" # <- Replace this with the port from LMStudio
|
||||
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik
|
||||
|
||||
mkdir -p ~/.openhands-state && echo '{"language":"en","agent":"CodeActAgent","max_iterations":null,"security_analyzer":null,"confirmation_mode":false,"llm_model":"lm_studio/'$LMSTUDIO_MODEL_NAME'","llm_api_key":"dummy","llm_base_url":"'$LMSTUDIO_URL/v1'","remote_runtime_resource_factor":null,"github_token":null,"enable_default_condenser":true,"user_consents_to_analytics":true}' > ~/.openhands-state/settings.json
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.41-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.43
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
```
|
||||
|
||||
Once your server is running -- you can visit `http://localhost:3000` in your browser to use OpenHands with local Devstral model:
|
||||
```
|
||||
Digest: sha256:e72f9baecb458aedb9afc2cd5bc935118d1868719e55d50da73190d3a85c674f
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.43
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.41
|
||||
Starting OpenHands...
|
||||
Running OpenHands as root
|
||||
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Getting Started
|
||||
description: Getting started with running OpenHands on your own.
|
||||
description: Getting started with running OpenHands locally.
|
||||
---
|
||||
|
||||
## Recommended Methods for Running Openhands on Your Local System
|
||||
@@ -10,7 +10,6 @@ description: Getting started with running OpenHands on your own.
|
||||
- MacOS with [Docker Desktop support](https://docs.docker.com/desktop/setup/install/mac-install/#system-requirements)
|
||||
- Linux
|
||||
- Windows with [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) and [Docker Desktop support](https://docs.docker.com/desktop/setup/install/windows-install/#system-requirements)
|
||||
- Windows without WSL (see [Windows Without WSL Guide](/usage/windows-without-wsl))
|
||||
|
||||
A system with a modern processor and a minimum of **4GB RAM** is recommended to run OpenHands.
|
||||
|
||||
@@ -56,10 +55,6 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
|
||||
The docker command below to start the app must be run inside the WSL terminal.
|
||||
</Note>
|
||||
|
||||
**Alternative: Windows without WSL**
|
||||
|
||||
If you prefer to run OpenHands on Windows without WSL or Docker, see our [Windows Without WSL Guide](/usage/windows-without-wsl).
|
||||
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
@@ -67,17 +62,17 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
|
||||
### Start the App
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.40-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.43-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.40-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.43
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.40
|
||||
```
|
||||
|
||||
You'll find OpenHands running at http://localhost:3000!
|
||||
@@ -122,24 +117,10 @@ OpenHands requires an API key to access most language models. Here's how to get
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Local LLM (e.g. LM Studio, llama.cpp, Ollama)">
|
||||
|
||||
If your local LLM server isn’t behind an authentication proxy, you can enter any value as the API key (e.g. `local-key`, `test123`) — it won’t be used.
|
||||
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
Consider setting usage limits to control costs.
|
||||
|
||||
#### Using a Local LLM
|
||||
|
||||
<Note>
|
||||
Effective use of local models for agent tasks requires capable hardware, along with models specifically tuned for instruction-following and agent-style behavior.
|
||||
</Note>
|
||||
|
||||
To run OpenHands with a locally hosted language model instead of a cloud provider, see the [Local LLMs guide](/usage/llms/local-llms) for setup instructions.
|
||||
|
||||
#### Setting Up Search Engine
|
||||
|
||||
OpenHands can be configured to use a search engine to allow the agent to search the web for information when needed.
|
||||
@@ -151,6 +132,7 @@ To enable search functionality in OpenHands:
|
||||
|
||||
For more details, see the [Search Engine Setup](/usage/search-engine-setup) guide.
|
||||
|
||||
|
||||
Now you're ready to [get started with OpenHands](/usage/getting-started).
|
||||
|
||||
### Versions
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
---
|
||||
title: Windows Without WSL
|
||||
description: Running OpenHands GUI on Windows without using WSL or Docker
|
||||
---
|
||||
|
||||
# Running OpenHands GUI on Windows Without WSL
|
||||
|
||||
This guide provides step-by-step instructions for running OpenHands on a Windows machine without using WSL or Docker.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Windows 10/11** - A modern Windows operating system
|
||||
2. **PowerShell 7+** - While Windows PowerShell comes pre-installed on Windows 10/11, PowerShell 7+ is strongly recommended to avoid compatibility issues (see Troubleshooting section for "System.Management.Automation" errors)
|
||||
3. **.NET Core Runtime** - Required for the PowerShell integration via pythonnet
|
||||
4. **Python 3.12 or 3.13** - Python 3.12 or 3.13 is required (Python 3.14 is not supported due to pythonnet compatibility)
|
||||
5. **Git** - For cloning the repository and version control
|
||||
6. **Node.js and npm** - For running the frontend
|
||||
|
||||
## Step 1: Install Required Software
|
||||
|
||||
1. **Install Python 3.12 or 3.13**
|
||||
- Download Python 3.12.x or 3.13.x from [python.org](https://www.python.org/downloads/)
|
||||
- During installation, check "Add Python to PATH"
|
||||
- Verify installation by opening PowerShell and running:
|
||||
```powershell
|
||||
python --version
|
||||
```
|
||||
|
||||
2. **Install PowerShell 7**
|
||||
- Download and install PowerShell 7 from the [official PowerShell GitHub repository](https://github.com/PowerShell/PowerShell/releases)
|
||||
- Choose the MSI installer appropriate for your system (x64 for most modern computers)
|
||||
- Run the installer with default options
|
||||
- Verify installation by opening a new terminal and running:
|
||||
```powershell
|
||||
pwsh --version
|
||||
```
|
||||
- Using PowerShell 7 (pwsh) instead of Windows PowerShell will help avoid "System.Management.Automation" errors
|
||||
|
||||
3. **Install .NET Core Runtime**
|
||||
- Download and install the .NET Core Runtime from [Microsoft's .NET download page](https://dotnet.microsoft.com/download)
|
||||
- Choose the latest .NET Core Runtime (not SDK)
|
||||
- Verify installation by opening PowerShell and running:
|
||||
```powershell
|
||||
dotnet --info
|
||||
```
|
||||
- This step is required for the PowerShell integration via pythonnet. Without it, OpenHands will fall back to a more limited PowerShell implementation.
|
||||
|
||||
4. **Install Git**
|
||||
- Download Git from [git-scm.com](https://git-scm.com/download/win)
|
||||
- Use default installation options
|
||||
- Verify installation:
|
||||
```powershell
|
||||
git --version
|
||||
```
|
||||
|
||||
5. **Install Node.js and npm**
|
||||
- Download Node.js from [nodejs.org](https://nodejs.org/) (LTS version recommended)
|
||||
- During installation, accept the default options which will install npm as well
|
||||
- Verify installation:
|
||||
```powershell
|
||||
node --version
|
||||
npm --version
|
||||
```
|
||||
|
||||
6. **Install Poetry**
|
||||
- Open PowerShell as Administrator and run:
|
||||
```powershell
|
||||
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
|
||||
```
|
||||
- Add Poetry to your PATH:
|
||||
```powershell
|
||||
$env:Path += ";$env:APPDATA\Python\Scripts"
|
||||
```
|
||||
- Verify installation:
|
||||
```powershell
|
||||
poetry --version
|
||||
```
|
||||
|
||||
## Step 2: Clone and Set Up OpenHands
|
||||
|
||||
1. **Clone the Repository**
|
||||
```powershell
|
||||
git clone https://github.com/All-Hands-AI/OpenHands.git
|
||||
cd OpenHands
|
||||
```
|
||||
|
||||
2. **Install Dependencies**
|
||||
```powershell
|
||||
poetry install
|
||||
```
|
||||
|
||||
This will install all required dependencies, including:
|
||||
- pythonnet - Required for Windows PowerShell integration
|
||||
- All other OpenHands dependencies
|
||||
|
||||
## Step 3: Run OpenHands
|
||||
|
||||
1. **Build the Frontend**
|
||||
```powershell
|
||||
cd frontend
|
||||
npm install
|
||||
npm run build
|
||||
cd ..
|
||||
```
|
||||
|
||||
This will build the frontend files that the backend will serve.
|
||||
|
||||
2. **Start the Backend**
|
||||
```powershell
|
||||
# Make sure to use PowerShell 7 (pwsh) instead of Windows PowerShell
|
||||
pwsh
|
||||
$env:RUNTIME="local"; poetry run uvicorn openhands.server.listen:app --host 0.0.0.0 --port 3000 --reload --reload-exclude "./workspace"
|
||||
```
|
||||
|
||||
This will start the OpenHands app using the local runtime with PowerShell integration, available at `localhost:3000`.
|
||||
|
||||
> **Note**: If you encounter a `RuntimeError: Directory './frontend/build' does not exist` error, make sure you've built the frontend first using the command above.
|
||||
|
||||
> **Important**: Using PowerShell 7 (pwsh) instead of Windows PowerShell is recommended to avoid "System.Management.Automation" errors. If you encounter this error, see the Troubleshooting section below.
|
||||
|
||||
3. **Alternatively, Run the Frontend in Development Mode (in a separate PowerShell window)**
|
||||
```powershell
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. **Access the OpenHands GUI**
|
||||
|
||||
Open your browser and navigate to:
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
> **Note**: If you're running the frontend in development mode (using `npm run dev`), use port 3001 instead: `http://localhost:3001`
|
||||
|
||||
## Limitations on Windows
|
||||
|
||||
When running OpenHands on Windows without WSL or Docker, be aware of the following limitations:
|
||||
|
||||
1. **Browser Tool Not Supported**: The browser tool is not currently supported on Windows.
|
||||
|
||||
2. **.NET Core Requirement**: The PowerShell integration requires .NET Core Runtime to be installed. If .NET Core is not available, OpenHands will automatically fall back to a more limited PowerShell implementation with reduced functionality.
|
||||
|
||||
3. **Interactive Shell Commands**: Some interactive shell commands may not work as expected. The PowerShell session implementation has limitations compared to the bash session used on Linux/macOS.
|
||||
|
||||
4. **Path Handling**: Windows uses backslashes (`\`) in paths, which may require adjustments when working with code examples designed for Unix-like systems.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "System.Management.Automation" Not Found Error
|
||||
|
||||
If you encounter an error message stating that "System.Management.Automation" was not found, this typically indicates that you have a minimal version of PowerShell installed or that the .NET components required for PowerShell integration are missing.
|
||||
|
||||
> **IMPORTANT**: This error is most commonly caused by using the built-in Windows PowerShell (powershell.exe) instead of PowerShell 7 (pwsh.exe). Even if you installed PowerShell 7 during the prerequisites, you may still be using the older Windows PowerShell by default.
|
||||
|
||||
To resolve this issue:
|
||||
|
||||
1. **Install the latest version of PowerShell 7** from the official Microsoft repository:
|
||||
- Visit [https://github.com/PowerShell/PowerShell/releases](https://github.com/PowerShell/PowerShell/releases)
|
||||
- Download and install the latest MSI package for your system architecture (x64 for most systems)
|
||||
- During installation, ensure you select the following options:
|
||||
- "Add PowerShell to PATH environment variable"
|
||||
- "Register Windows PowerShell 7 as the default shell"
|
||||
- "Enable PowerShell remoting"
|
||||
- The installer will place PowerShell 7 in `C:\Program Files\PowerShell\7` by default
|
||||
|
||||
2. **Restart your terminal or command prompt** to ensure the new PowerShell is available
|
||||
|
||||
3. **Verify the installation** by running:
|
||||
```powershell
|
||||
pwsh --version
|
||||
```
|
||||
|
||||
You should see output indicating PowerShell 7.x.x
|
||||
|
||||
4. **Run OpenHands using PowerShell 7** instead of Windows PowerShell:
|
||||
```powershell
|
||||
pwsh
|
||||
cd path\to\openhands
|
||||
$env:RUNTIME="local"; poetry run uvicorn openhands.server.listen:app --host 0.0.0.0 --port 3000 --reload --reload-exclude "./workspace"
|
||||
```
|
||||
|
||||
> **Note**: Make sure you're explicitly using `pwsh` (PowerShell 7) and not `powershell` (Windows PowerShell). The command prompt or terminal title should say "PowerShell 7" rather than just "Windows PowerShell".
|
||||
|
||||
5. **If the issue persists**, ensure that you have the .NET Runtime installed:
|
||||
- Download and install the latest .NET Runtime from [Microsoft's .NET download page](https://dotnet.microsoft.com/download)
|
||||
- Choose ".NET Runtime" (not SDK) version 6.0 or later
|
||||
- After installation, verify it's properly installed by running:
|
||||
```powershell
|
||||
dotnet --info
|
||||
```
|
||||
- Restart your computer after installation
|
||||
- Try running OpenHands again
|
||||
|
||||
6. **Ensure that the .NET Framework is properly installed** on your system:
|
||||
- Go to Control Panel > Programs > Programs and Features > Turn Windows features on or off
|
||||
- Make sure ".NET Framework 4.8 Advanced Services" is enabled
|
||||
- Click OK and restart if prompted
|
||||
|
||||
This error occurs because OpenHands uses the pythonnet package to interact with PowerShell, which requires the System.Management.Automation assembly from the .NET framework. A minimal PowerShell installation or older Windows PowerShell (rather than PowerShell 7+) might not include all the necessary components for this integration.
|
||||
@@ -74,24 +74,6 @@ If no condenser configuration is specified, the 'noop' condenser will be used by
|
||||
|
||||
For other configurations specific to evaluation, such as `save_trajectory_path`, these are typically set in the `get_config` function of the respective `run_infer.py` file for each benchmark.
|
||||
|
||||
### Enabling LLM-Based Editor Tools
|
||||
|
||||
The LLM-Based Editor tool (currently supported only for SWE-Bench) can be enabled by setting:
|
||||
```bash
|
||||
export ENABLE_LLM_EDITOR=true
|
||||
```
|
||||
|
||||
You can set the config for the Editor LLM as:
|
||||
```toml
|
||||
[llm.draft_editor]
|
||||
base_url = "http://localhost:9002/v1"
|
||||
model = "hosted_vllm/lite_coder_qwen_editor_3B"
|
||||
api_key = ""
|
||||
temperature = 0.7
|
||||
max_input_tokens = 10500
|
||||
max_output_tokens = 10500
|
||||
```
|
||||
|
||||
## Supported Benchmarks
|
||||
|
||||
The OpenHands evaluation harness supports a wide variety of benchmarks across [software engineering](#software-engineering), [web browsing](#web-browsing), [miscellaneous assistance](#misc-assistance), and [real-world](#real-world) tasks.
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<uploaded_files>
|
||||
/workspace/{{ workspace_dir_name }}
|
||||
</uploaded_files>
|
||||
|
||||
I've uploaded a python code repository in the directory {{ workspace_dir_name }}. Consider the following issue description:
|
||||
|
||||
<issue_description>
|
||||
{{ instance.problem_statement }}
|
||||
</issue_description>
|
||||
|
||||
Can you help me implement the necessary changes to the repository so that the requirements specified in the <issue_description> are met?
|
||||
I've already taken care of all changes to any of the test files described in the <issue_description>. This means you DON'T have to modify the testing logic or any of the tests in any way!
|
||||
Also the development Python environment is already set up for you (i.e., all dependencies already installed), so you don't need to install other packages.
|
||||
Your task is to make the minimal changes to non-test files in the /workspace/{{ workspace_dir_name }} directory to ensure the <issue_description> is satisfied.
|
||||
|
||||
Follow these phases to resolve the issue:
|
||||
|
||||
Phase 1. READING: read the problem and reword it in clearer terms
|
||||
1.1 If there are code or config snippets. Express in words any best practices or conventions in them.
|
||||
1.2 Hightlight message errors, method names, variables, file names, stack traces, and technical details.
|
||||
1.3 Explain the problem in clear terms.
|
||||
1.4 Enumerate the steps to reproduce the problem.
|
||||
1.5 Hightlight any best practices to take into account when testing and fixing the issue
|
||||
|
||||
Phase 2. RUNNING: install and run the tests on the repository
|
||||
2.1 Follow the readme
|
||||
2.2 Install the environment and anything needed
|
||||
2.2 Iterate and figure out how to run the tests
|
||||
|
||||
Phase 3. EXPLORATION: find the files that are related to the problem and possible solutions
|
||||
3.1 Use `grep` to search for relevant methods, classes, keywords and error messages.
|
||||
3.2 Identify all files related to the problem statement.
|
||||
3.3 Propose the methods and files to fix the issue and explain why.
|
||||
3.4 From the possible file locations, select the most likely location to fix the issue.
|
||||
|
||||
Phase 4. TEST CREATION: before implementing any fix, create a script to reproduce and verify the issue.
|
||||
4.1 Look at existing test files in the repository to understand the test format/structure.
|
||||
4.2 Create a minimal reproduction script that reproduces the located issue.
|
||||
4.3 Run the reproduction script to confirm you are reproducing the issue.
|
||||
4.4 Adjust the reproduction script as necessary.
|
||||
|
||||
Phase 5. FIX ANALYSIS: state clearly the problem and how to fix it
|
||||
5.1 State clearly what the problem is.
|
||||
5.2 State clearly where the problem is located.
|
||||
5.3 State clearly how the test reproduces the issue.
|
||||
5.4 State clearly the best practices to take into account in the fix.
|
||||
5.5 State clearly how to fix the problem.
|
||||
|
||||
Phase 6. FIX IMPLEMENTATION: Edit the source code to implement your chosen solution.
|
||||
6.1 Make minimal, focused changes to fix the issue.
|
||||
|
||||
Phase 7. VERIFICATION: Test your implementation thoroughly.
|
||||
7.1 Run your reproduction script to verify the fix works.
|
||||
7.2 Add edge cases to your test script to ensure comprehensive coverage.
|
||||
7.3 Run existing tests related to the modified code to ensure you haven't broken anything.
|
||||
|
||||
8. FINAL REVIEW: Carefully re-read the problem description and compare your changes with the base commit {{ instance.base_commit }}.
|
||||
8.1 Ensure you've fully addressed all requirements.
|
||||
8.2 Run any tests in the repository related to:
|
||||
8.2.1 The issue you are fixing
|
||||
8.2.2 The files you modified
|
||||
8.2.3 The functions you changed
|
||||
8.3 If any tests fail, revise your implementation until all tests pass
|
||||
|
||||
Be thorough in your exploration, testing, and reasoning. It's fine if your thinking process is lengthy - quality and completeness are more important than brevity.
|
||||
@@ -1,65 +0,0 @@
|
||||
<uploaded_files>
|
||||
/workspace/{{ workspace_dir_name }}
|
||||
</uploaded_files>
|
||||
|
||||
I've uploaded a python code repository in the directory {{ workspace_dir_name }}. Consider the following issue description:
|
||||
|
||||
<issue_description>
|
||||
{{ instance.problem_statement }}
|
||||
</issue_description>
|
||||
|
||||
Can you help me implement the necessary changes to the repository so that the requirements specified in the <issue_description> are met?
|
||||
I've already taken care of all changes to any of the test files described in the <issue_description>. This means you DON'T have to modify the testing logic or any of the tests in any way!
|
||||
Also the development Python environment is already set up for you (i.e., all dependencies already installed), so you don't need to install other packages.
|
||||
Your task is to make the minimal changes to non-test files in the /workspace/{{ workspace_dir_name }} directory to ensure the <issue_description> is satisfied.
|
||||
|
||||
Follow these phases to resolve the issue:
|
||||
|
||||
Phase 1. READING: read the problem and reword it in clearer terms
|
||||
1.1 If there are code or config snippets. Express in words any best practices or conventions in them.
|
||||
1.2 Hightlight message errors, method names, variables, file names, stack traces, and technical details.
|
||||
1.3 Explain the problem in clear terms.
|
||||
1.4 Enumerate the steps to reproduce the problem.
|
||||
1.5 Hightlight any best practices to take into account when testing and fixing the issue
|
||||
|
||||
Phase 2. RUNNING: install and run the tests on the repository
|
||||
2.1 Follow the readme
|
||||
2.2 Install the environment and anything needed
|
||||
2.2 Iterate and figure out how to run the tests
|
||||
|
||||
Phase 3. EXPLORATION: find the files that are related to the problem and possible solutions
|
||||
3.1 Use `grep` to search for relevant methods, classes, keywords and error messages.
|
||||
3.2 Identify all files related to the problem statement.
|
||||
3.3 Propose the methods and files to fix the issue and explain why.
|
||||
3.4 From the possible file locations, select the most likely location to fix the issue.
|
||||
|
||||
Phase 4. TEST CREATION: before implementing any fix, create a script to reproduce and verify the issue.
|
||||
4.1 Look at existing test files in the repository to understand the test format/structure.
|
||||
4.2 Create a minimal reproduction script that reproduces the located issue.
|
||||
4.3 Run the reproduction script to confirm you are reproducing the issue.
|
||||
4.4 Adjust the reproduction script as necessary.
|
||||
|
||||
Phase 5. FIX ANALYSIS: state clearly the problem and how to fix it
|
||||
5.1 State clearly what the problem is.
|
||||
5.2 State clearly where the problem is located.
|
||||
5.3 State clearly how the test reproduces the issue.
|
||||
5.4 State clearly the best practices to take into account in the fix.
|
||||
5.5 State clearly how to fix the problem.
|
||||
|
||||
Phase 6. FIX IMPLEMENTATION: Edit the source code to implement your chosen solution.
|
||||
6.1 Make minimal, focused changes to fix the issue.
|
||||
|
||||
Phase 7. VERIFICATION: Test your implementation thoroughly.
|
||||
7.1 Run your reproduction script to verify the fix works.
|
||||
7.2 Add edge cases to your test script to ensure comprehensive coverage.
|
||||
7.3 Run existing tests related to the modified code to ensure you haven't broken anything.
|
||||
|
||||
8. FINAL REVIEW: Carefully re-read the problem description and compare your changes with the base commit {{ instance.base_commit }}.
|
||||
8.1 Ensure you've fully addressed all requirements.
|
||||
8.2 Run any tests in the repository related to:
|
||||
8.2.1 The issue you are fixing
|
||||
8.2.2 The files you modified
|
||||
8.2.3 The functions you changed
|
||||
8.3 If any tests fail, revise your implementation until all tests pass
|
||||
|
||||
Be thorough in your exploration, testing, and reasoning. It's fine if your thinking process is lengthy - quality and completeness are more important than brevity.
|
||||
@@ -1,45 +0,0 @@
|
||||
# Task: Fix Issue in Python Repository
|
||||
|
||||
## Repository Context
|
||||
You are provided with a Python code repository that contains an issue requiring your attention. The repository is located in a sandboxed environment, and you have access to the codebase to implement the necessary changes.
|
||||
The code repository is located at: `/workspace/{{ workspace_dir_name }}`
|
||||
(This path is provided for context; use file system tools to confirm paths before access).
|
||||
|
||||
## Goal
|
||||
Your goal is to fix the issue described in the **Issue Description** section below. Implement the necessary changes to **non-test files only** within the repository, ensuring that **all relevant tests pass** after your changes.
|
||||
|
||||
## Key Requirements & Constraints
|
||||
|
||||
1. **Understand the problem** very well: it is a bug report, and you know humans don't always write good descriptions. Explore the codebase to understand the related code and the problem in depth. It is possible that the solution needs to be a bit more extensive than just the stated text. Don't exagerate though: don't do unrelated refactoring, but also don't interpret the description too strictly.
|
||||
2. **Focus on the issues:** Implement the fix focusing on non-test files related to the issue.
|
||||
2. **Environment Ready:** The Python environment is pre-configured with all dependencies. Do not install packages.
|
||||
3. **Mandatory Testing Procedure:**
|
||||
* **Create Test to Reproduce the Issue:** *Before* implementing any fix, you MUST create a *new test* (separate from existing tests) that specifically reproduces the issue.
|
||||
* Take existing tests as example to understand the testing format/structure.
|
||||
* Enhance this test with edge cases.
|
||||
* Run this test to confirm reproduction.
|
||||
* **Verify Fix:** After implementing the fix, run your test again to verify the issue is resolved.
|
||||
* **Identify ALL Relevant Tests:** You MUST perform a **dedicated search and analysis** to identify **all** existing unit tests potentially affected by your changes. This includes:
|
||||
* Tests in the same module/directory as the changed files (e.g., `tests/` subdirectories).
|
||||
* Tests explicitly importing or using the modified code/classes/functions.
|
||||
* Tests mentioned in the issue description or related documentation.
|
||||
* Tests covering functionalities that *depend on* the modified code (analyze callers/dependencies if necessary).
|
||||
**If you cannot confidently identify a specific subset, you MUST identify and plan to run the entire test suite for the modified application or module(s). State your identified test scope clearly.**
|
||||
* **Run Identified Relevant Tests:** You MUST execute the **complete set** of relevant existing unit tests you identified in the previous step. Ensure you are running the *correct and comprehensive set* of tests. You MUST NOT modify these existing tests.
|
||||
* **Final Check & Verification:** Before finishing, ensure **all** identified relevant existing tests pass. **Explicitly confirm that you have considered potential omissions in your test selection and believe the executed tests comprehensively cover the impact of your changes.** Failing to identify and run the *complete* relevant set constitutes a failure. If any identified tests fail, revise your fix. Passing all relevant tests is the primary measure of success.
|
||||
4. **Defensive Programming:** Actively practice defensive programming: anticipate and handle potential edge cases, unexpected inputs, and different ways the affected code might be called **to ensure the fix works reliably and allows relevant tests to pass.** Analyze the potential impact on other parts of the codebase.
|
||||
5. **Final Review:** Compare your solution against the original issue and the base commit ({{ instance.base_commit }}) to ensure completeness and test passage.
|
||||
|
||||
## General Workflow Guidance
|
||||
|
||||
* Prioritize understanding the problem, exploring the code, planning your fix, implementing it carefully using the required diff format, and **thoroughly testing** according to the **Mandatory Testing Procedure**.
|
||||
* Consider trade-offs between different solutions. The goal is a **robust change that makes the relevant tests pass.** Quality, correctness, and reliability are key.
|
||||
* Actively practice defensive programming: anticipate and handle potential edge cases, unexpected inputs, and different ways the affected code might be called **to ensure the fix works reliably and allows relevant tests to pass.** Analyze the potential impact on other parts of the codebase.
|
||||
|
||||
* IMPORTANT: Your solution will be tested by additional hidden tests, so do not assume the task is complete just because visible tests pass! Refine the solution until you are confident that it is robust and comprehensive according to the **Defensive Programming** requirement.
|
||||
|
||||
## Final Note
|
||||
Be thorough in your exploration, testing, and reasoning. It's fine if your thinking process is lengthy - quality and completeness are more important than brevity.
|
||||
|
||||
## Issue Description
|
||||
{{ instance.problem_statement }}
|
||||
@@ -1,80 +0,0 @@
|
||||
You will be tasked to fix an issue from an open-source repository.
|
||||
|
||||
Your thinking should be thorough and so it's fine if it's very long. You can think step by step before and after each action you decide to take.
|
||||
|
||||
You MUST iterate and keep going until the problem is solved.
|
||||
|
||||
You already have everything you need to solve this problem in the /workspace/{{ workspace_dir_name }} folder, even without internet connection. I want you to fully solve this autonomously before coming back to me.
|
||||
|
||||
Only terminate your turn when you are sure that the problem is solved. Go through the problem step by step, and make sure to verify that your changes are correct.
|
||||
NEVER end your turn without having solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.
|
||||
|
||||
THE PROBLEM CAN DEFINITELY BE SOLVED WITHOUT THE INTERNET.
|
||||
|
||||
Take your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Your solution must be perfect. If not, continue working on it.
|
||||
At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.
|
||||
|
||||
You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
|
||||
|
||||
# Workflow
|
||||
|
||||
## High-Level Problem Solving Strategy
|
||||
|
||||
1. Understand the problem deeply. Carefully read the issue and think critically about what is required.
|
||||
2. Investigate the codebase. Explore relevant files, search for key functions, and gather context.
|
||||
3. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps.
|
||||
4. Implement the fix incrementally. Make small, testable code changes.
|
||||
5. Debug as needed. Use debugging techniques to isolate and resolve issues.
|
||||
6. Test frequently. Run tests after each change to verify correctness.
|
||||
7. Iterate until the root cause is fixed and all tests pass.
|
||||
8. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness,
|
||||
and remember there are hidden tests that must also pass before the solution is truly complete.
|
||||
|
||||
Refer to the detailed sections below for more information on each step.
|
||||
|
||||
## 1. Deeply Understand the Problem
|
||||
Carefully read the issue and think hard about a plan to solve it before coding.
|
||||
|
||||
## 2. Codebase Investigation
|
||||
- Explore relevant files and directories.
|
||||
- Search for key functions, classes, or variables related to the issue.
|
||||
- Read and understand relevant code snippets.
|
||||
- Identify the root cause of the problem.
|
||||
- Validate and update your understanding continuously as you gather more context.
|
||||
|
||||
## 3. Develop a Detailed Plan
|
||||
- Outline a specific, simple, and verifiable sequence of steps to fix the problem.
|
||||
- Break down the fix into small, incremental changes.
|
||||
|
||||
## 4. Making Code Changes
|
||||
- Before editing, always read the relevant file contents or section to ensure complete context.
|
||||
- If a patch is not applied correctly, attempt to reapply it.
|
||||
- Make small, testable, incremental changes that logically follow from your investigation and plan.
|
||||
|
||||
## 5. Debugging
|
||||
- Make code changes only if you have high confidence they can solve the problem
|
||||
- When debugging, try to determine the root cause rather than addressing symptoms
|
||||
- Debug for as long as needed to identify the root cause and identify a fix
|
||||
- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening
|
||||
- To test hypotheses, you can also add test statements or functions
|
||||
- Revisit your assumptions if unexpected behavior occurs.
|
||||
|
||||
## 6. Testing
|
||||
- Run tests frequently using `python3 run_tests.py` (or equivalent).
|
||||
- After each change, verify correctness by running relevant tests.
|
||||
- If tests fail, analyze failures and revise your patch.
|
||||
- Write additional tests if needed to capture important behaviors or edge cases.
|
||||
- Ensure all tests pass before finalizing.
|
||||
|
||||
## 7. Final Verification
|
||||
- Confirm the root cause is fixed.
|
||||
- Review your solution for logic correctness and robustness.
|
||||
- Iterate until you are extremely confident the fix is complete and all tests pass.
|
||||
|
||||
## 8. Final Reflection and Additional Testing
|
||||
- Reflect carefully on the original intent of the user and the problem statement.
|
||||
- Think about potential edge cases or scenarios that may not be covered by existing tests.
|
||||
- Write additional tests that would need to pass to fully validate the correctness of your solution.
|
||||
- Run these new tests and ensure they all pass.
|
||||
- Be aware that there are additional hidden tests that must also pass for the solution to be successful.
|
||||
- Do not assume the task is complete just because the visible tests pass; continue refining until you are confident the fix is robust and comprehensive.
|
||||
@@ -1,19 +0,0 @@
|
||||
<uploaded_files>
|
||||
/workspace/{{ workspace_dir_name }}
|
||||
</uploaded_files>
|
||||
I've uploaded a python code repository in the directory {{ workspace_dir_name }}. Consider the following issue description:
|
||||
|
||||
<issue_description>
|
||||
{{ instance.problem_statement }}
|
||||
</issue_description>
|
||||
|
||||
|
||||
Can you help me implement the necessary changes to the repository to test whether the issue in <issue_description> was resolved?
|
||||
I will take care of all changes to any of the non-test files. This means you DON'T have to modify the actual logic and ONLY have to update test logic and tests!
|
||||
Your task is to make the minimal changes to tests files in the /workspace directory to reproduce the issue in the <issue_description>, i.e., such that the generated tests fail in the current state (where the issue is unresolved) and pass when the issue will be resolved.
|
||||
Follow these steps to reproduce the issue:
|
||||
1. As a first step, it might be a good idea to explore the repo to familiarize yourself with its structure.
|
||||
2. Create a script `reproduction.py` to reproduce the error and execute it with `python reproduction.py` using the BashTool, to confirm the error
|
||||
3. Edit the sourcecode of the repo to integrate your reproduction script into the test framework
|
||||
4. Run the test framework and make sure your tests fail! Only submit FAILING tests! Never submit passing tests.
|
||||
{{ test_instructions }}Your thinking should be thorough and so it's fine if it's very long.
|
||||
@@ -8,7 +8,6 @@ from typing import Any, Literal
|
||||
import pandas as pd
|
||||
import toml
|
||||
from datasets import load_dataset
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
import openhands.agenthub
|
||||
from evaluation.benchmarks.swe_bench.binary_patch_utils import (
|
||||
@@ -43,7 +42,7 @@ from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser
|
||||
get_parser,
|
||||
)
|
||||
from openhands.core.config.condenser_config import NoOpCondenserConfig
|
||||
from openhands.core.config.utils import get_condenser_config_arg
|
||||
@@ -63,7 +62,6 @@ from openhands.utils.shutdown_listener import sleep_if_should_continue
|
||||
|
||||
USE_HINT_TEXT = os.environ.get('USE_HINT_TEXT', 'false').lower() == 'true'
|
||||
RUN_WITH_BROWSING = os.environ.get('RUN_WITH_BROWSING', 'false').lower() == 'true'
|
||||
ENABLE_LLM_EDITOR = os.environ.get('ENABLE_LLM_EDITOR', 'false').lower() == 'true'
|
||||
BenchMode = Literal['swe', 'swt', 'swt-ci']
|
||||
|
||||
|
||||
@@ -79,48 +77,101 @@ def _get_swebench_workspace_dir_name(instance: pd.Series) -> str:
|
||||
def get_instruction(instance: pd.Series, metadata: EvalMetadata) -> MessageAction:
|
||||
workspace_dir_name = _get_swebench_workspace_dir_name(instance)
|
||||
mode = metadata.details['mode']
|
||||
llm_model = metadata.llm_config.model
|
||||
|
||||
# Determine the template file based on mode and LLM
|
||||
if mode.startswith('swt'):
|
||||
template_name = 'swt.j2'
|
||||
elif mode == 'swe':
|
||||
if 'claude' in llm_model:
|
||||
template_name = 'swe_claude.j2'
|
||||
elif 'gemini' in llm_model:
|
||||
template_name = 'swe_gemini.j2'
|
||||
elif 'gpt-4.1' in llm_model:
|
||||
template_name = 'swe_gpt4.j2'
|
||||
else:
|
||||
template_name = 'swe_default.j2' # Default for 'swe' mode (regular swe-bench)
|
||||
else:
|
||||
# Fallback or error handling if mode is unexpected
|
||||
logger.error(f"Unexpected evaluation mode: {mode}. Falling back to default.")
|
||||
template_name = 'swe_default.j2'
|
||||
|
||||
# Set up Jinja2 environment
|
||||
# Assuming templates are in 'evaluation/benchmarks/swe_bench/prompts' relative to this script
|
||||
prompts_dir = os.path.join(os.path.dirname(__file__), 'prompts')
|
||||
env = Environment(loader=FileSystemLoader(prompts_dir))
|
||||
template = env.get_template(template_name)
|
||||
|
||||
# Prepare context for rendering
|
||||
context = {
|
||||
'instance': instance,
|
||||
'workspace_dir_name': workspace_dir_name,
|
||||
'metadata': metadata, # Pass metadata if needed in templates
|
||||
}
|
||||
|
||||
# Add specific context for swt-ci mode if needed
|
||||
if mode == 'swt-ci':
|
||||
context['test_instructions'] = (
|
||||
test_instructions = (
|
||||
f'The following command can be used to run the tests: `{list(MAP_REPO_TO_TEST_FRAMEWORK_VERBOSE[instance.repo].values())[0]}`. Make sure they fail in the expected way.\n'
|
||||
if mode.endswith('ci')
|
||||
else ''
|
||||
)
|
||||
else:
|
||||
context['test_instructions'] = '' # Ensure it's defined for other modes
|
||||
instruction = f"""\
|
||||
<uploaded_files>
|
||||
/workspace/{workspace_dir_name}
|
||||
</uploaded_files>
|
||||
I've uploaded a python code repository in the directory {workspace_dir_name}. Consider the following issue description:
|
||||
|
||||
# Render the instruction
|
||||
instruction = template.render(context)
|
||||
<issue_description>
|
||||
{instance.problem_statement}
|
||||
</issue_description>
|
||||
|
||||
|
||||
Can you help me implement the necessary changes to the repository to test whether the issue in <issue_description> was resolved?
|
||||
I will take care of all changes to any of the non-test files. This means you DON'T have to modify the actual logic and ONLY have to update test logic and tests!
|
||||
Your task is to make the minimal changes to tests files in the /workspace directory to reproduce the issue in the <issue_description>, i.e., such that the generated tests fail in the current state (where the issue is unresolved) and pass when the issue will be resolved.
|
||||
Follow these steps to reproduce the issue:
|
||||
1. As a first step, it might be a good idea to explore the repo to familiarize yourself with its structure.
|
||||
2. Create a script `reproduction.py` to reproduce the error and execute it with `python reproduction.py` using the BashTool, to confirm the error
|
||||
3. Edit the sourcecode of the repo to integrate your reproduction script into the test framework
|
||||
4. Run the test framework and make sure your tests fail! Only submit FAILING tests! Never submit passing tests.
|
||||
{test_instructions}Your thinking should be thorough and so it's fine if it's very long.
|
||||
"""
|
||||
else:
|
||||
instruction = f"""
|
||||
<uploaded_files>
|
||||
/workspace/{workspace_dir_name}
|
||||
</uploaded_files>
|
||||
|
||||
I've uploaded a python code repository in the directory {workspace_dir_name}. Consider the following issue description:
|
||||
|
||||
<issue_description>
|
||||
{instance.problem_statement}
|
||||
</issue_description>
|
||||
|
||||
Can you help me implement the necessary changes to the repository so that the requirements specified in the <issue_description> are met?
|
||||
I've already taken care of all changes to any of the test files described in the <issue_description>. This means you DON'T have to modify the testing logic or any of the tests in any way!
|
||||
Also the development Python environment is already set up for you (i.e., all dependencies already installed), so you don't need to install other packages.
|
||||
Your task is to make the minimal changes to non-test files in the /workspace/{workspace_dir_name} directory to ensure the <issue_description> is satisfied.
|
||||
|
||||
Follow these phases to resolve the issue:
|
||||
|
||||
Phase 1. READING: read the problem and reword it in clearer terms
|
||||
1.1 If there are code or config snippets. Express in words any best practices or conventions in them.
|
||||
1.2 Hightlight message errors, method names, variables, file names, stack traces, and technical details.
|
||||
1.3 Explain the problem in clear terms.
|
||||
1.4 Enumerate the steps to reproduce the problem.
|
||||
1.5 Hightlight any best practices to take into account when testing and fixing the issue
|
||||
|
||||
Phase 2. RUNNING: install and run the tests on the repository
|
||||
2.1 Follow the readme
|
||||
2.2 Install the environment and anything needed
|
||||
2.2 Iterate and figure out how to run the tests
|
||||
|
||||
Phase 3. EXPLORATION: find the files that are related to the problem and possible solutions
|
||||
3.1 Use `grep` to search for relevant methods, classes, keywords and error messages.
|
||||
3.2 Identify all files related to the problem statement.
|
||||
3.3 Propose the methods and files to fix the issue and explain why.
|
||||
3.4 From the possible file locations, select the most likely location to fix the issue.
|
||||
|
||||
Phase 4. TEST CREATION: before implementing any fix, create a script to reproduce and verify the issue.
|
||||
4.1 Look at existing test files in the repository to understand the test format/structure.
|
||||
4.2 Create a minimal reproduction script that reproduces the located issue.
|
||||
4.3 Run the reproduction script to confirm you are reproducing the issue.
|
||||
4.4 Adjust the reproduction script as necessary.
|
||||
|
||||
Phase 5. FIX ANALYSIS: state clearly the problem and how to fix it
|
||||
5.1 State clearly what the problem is.
|
||||
5.2 State clearly where the problem is located.
|
||||
5.3 State clearly how the test reproduces the issue.
|
||||
5.4 State clearly the best practices to take into account in the fix.
|
||||
5.5 State clearly how to fix the problem.
|
||||
|
||||
Phase 6. FIX IMPLEMENTATION: Edit the source code to implement your chosen solution.
|
||||
6.1 Make minimal, focused changes to fix the issue.
|
||||
|
||||
Phase 7. VERIFICATION: Test your implementation thoroughly.
|
||||
7.1 Run your reproduction script to verify the fix works.
|
||||
7.2 Add edge cases to your test script to ensure comprehensive coverage.
|
||||
7.3 Run existing tests related to the modified code to ensure you haven't broken anything.
|
||||
|
||||
8. FINAL REVIEW: Carefully re-read the problem description and compare your changes with the base commit {instance['base_commit']}.
|
||||
8.1 Ensure you've fully addressed all requirements.
|
||||
8.2 Run any tests in the repository related to:
|
||||
8.2.1 The issue you are fixing
|
||||
8.2.2 The files you modified
|
||||
8.2.3 The functions you changed
|
||||
8.3 If any tests fail, revise your implementation until all tests pass
|
||||
|
||||
Be thorough in your exploration, testing, and reasoning. It's fine if your thinking process is lengthy - quality and completeness are more important than brevity.
|
||||
"""
|
||||
|
||||
if RUN_WITH_BROWSING:
|
||||
instruction += (
|
||||
@@ -203,19 +254,15 @@ def get_config(
|
||||
workspace_base=None,
|
||||
workspace_mount_path=None,
|
||||
)
|
||||
|
||||
config.set_llm_config(
|
||||
update_llm_config_for_completions_logging(
|
||||
metadata.llm_config, metadata.eval_output_dir, instance['instance_id']
|
||||
)
|
||||
)
|
||||
# get 'draft_editor' config if exists
|
||||
config.set_llm_config(get_llm_config_arg('draft_editor'), 'draft_editor')
|
||||
|
||||
agent_config = AgentConfig(
|
||||
enable_jupyter=False,
|
||||
enable_browsing=RUN_WITH_BROWSING,
|
||||
enable_llm_editor=ENABLE_LLM_EDITOR,
|
||||
enable_llm_editor=False,
|
||||
enable_mcp=False,
|
||||
condenser=metadata.condenser_config,
|
||||
enable_prompt_extensions=False,
|
||||
|
||||
@@ -921,7 +921,7 @@ SPECS_PYDICOM.update(
|
||||
|
||||
SPECS_HUMANEVAL = {k: {'python': '3.9', 'test_cmd': 'python'} for k in ['1.0']}
|
||||
|
||||
# Constants - Task Instance Installation Environment
|
||||
# Constants - Task Instance Instllation Environment
|
||||
MAP_REPO_VERSION_TO_SPECS: dict[str, dict[str, Any]] = {
|
||||
'astropy/astropy': SPECS_ASTROPY,
|
||||
'dbt-labs/dbt-core': SPECS_DBT_CORE,
|
||||
|
||||
@@ -539,7 +539,7 @@ if __name__ == '__main__':
|
||||
if args.llm_config:
|
||||
llm_config = get_llm_config_arg(args.llm_config)
|
||||
llm_config.log_completions = True
|
||||
# modify_params must be False for evaluation purpose, for reproducibility and accuracy of results
|
||||
# modify_params must be False for evaluation purpose, for reproducibility and accurancy of results
|
||||
llm_config.modify_params = False
|
||||
|
||||
if llm_config is None:
|
||||
|
||||
@@ -4,6 +4,7 @@ import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import type { Message } from "#/message";
|
||||
import { SUGGESTIONS } from "#/utils/suggestions";
|
||||
import { WsClientProviderStatus } from "#/context/ws-client-provider";
|
||||
import { ChatInterface } from "#/components/features/chat/chat-interface";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@@ -18,7 +19,7 @@ describe("Empty state", () => {
|
||||
const { useWsClient: useWsClientMock } = vi.hoisted(() => ({
|
||||
useWsClient: vi.fn(() => ({
|
||||
send: sendMock,
|
||||
status: "CONNECTED",
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
isLoadingMessages: false,
|
||||
})),
|
||||
}));
|
||||
@@ -63,7 +64,7 @@ describe("Empty state", () => {
|
||||
// this is to test that the message is in the UI before the socket is called
|
||||
useWsClientMock.mockImplementation(() => ({
|
||||
send: sendMock,
|
||||
status: "CONNECTED",
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
isLoadingMessages: false,
|
||||
}));
|
||||
const user = userEvent.setup();
|
||||
@@ -86,7 +87,7 @@ describe("Empty state", () => {
|
||||
async () => {
|
||||
useWsClientMock.mockImplementation(() => ({
|
||||
send: sendMock,
|
||||
status: "CONNECTED",
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
isLoadingMessages: false,
|
||||
}));
|
||||
const user = userEvent.setup();
|
||||
@@ -100,7 +101,7 @@ describe("Empty state", () => {
|
||||
|
||||
useWsClientMock.mockImplementation(() => ({
|
||||
send: sendMock,
|
||||
status: "CONNECTED",
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
isLoadingMessages: false,
|
||||
}));
|
||||
rerender(<ChatInterface />);
|
||||
|
||||
@@ -478,7 +478,7 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
conversationStatus="RUNNING"
|
||||
status="RUNNING"
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ describe("ConversationPanel", () => {
|
||||
last_updated_at: "2021-10-01T12:00:00Z",
|
||||
created_at: "2021-10-01T12:00:00Z",
|
||||
status: "STOPPED" as const,
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -61,7 +60,6 @@ describe("ConversationPanel", () => {
|
||||
last_updated_at: "2021-10-02T12:00:00Z",
|
||||
created_at: "2021-10-02T12:00:00Z",
|
||||
status: "STOPPED" as const,
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -74,7 +72,6 @@ describe("ConversationPanel", () => {
|
||||
last_updated_at: "2021-10-03T12:00:00Z",
|
||||
created_at: "2021-10-03T12:00:00Z",
|
||||
status: "STOPPED" as const,
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -161,7 +158,6 @@ describe("ConversationPanel", () => {
|
||||
last_updated_at: "2021-10-01T12:00:00Z",
|
||||
created_at: "2021-10-01T12:00:00Z",
|
||||
status: "STOPPED" as const,
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -174,7 +170,6 @@ describe("ConversationPanel", () => {
|
||||
last_updated_at: "2021-10-02T12:00:00Z",
|
||||
created_at: "2021-10-02T12:00:00Z",
|
||||
status: "STOPPED" as const,
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -187,7 +182,6 @@ describe("ConversationPanel", () => {
|
||||
last_updated_at: "2021-10-03T12:00:00Z",
|
||||
created_at: "2021-10-03T12:00:00Z",
|
||||
status: "STOPPED" as const,
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
|
||||
@@ -65,7 +65,6 @@ describe("WsClientProvider", () => {
|
||||
last_updated_at: "2021-10-01T12:00:00Z",
|
||||
created_at: "2021-10-01T12:00:00Z",
|
||||
status: "RUNNING" as const,
|
||||
runtime_status: "STATUS$READY",
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
}}},
|
||||
|
||||
@@ -334,7 +334,10 @@ describe("Settings 404", () => {
|
||||
|
||||
renderHomeScreen();
|
||||
|
||||
expect(screen.queryByTestId("ai-config-modal")).not.toBeInTheDocument();
|
||||
// small hack to wait for the modal to not appear
|
||||
await expect(
|
||||
screen.findByTestId("ai-config-modal", {}, { timeout: 1000 }),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { heroui } from "@heroui/react";
|
||||
|
||||
export default heroui({
|
||||
defaultTheme: "dark",
|
||||
layout: {
|
||||
radius: {
|
||||
small: "5px",
|
||||
large: "20px",
|
||||
},
|
||||
},
|
||||
themes: {
|
||||
dark: {
|
||||
colors: {
|
||||
primary: "#4465DB",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
6530
frontend/package-lock.json
generated
@@ -1,39 +1,37 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.43.0",
|
||||
"version": "0.41.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.0-beta.7",
|
||||
"@heroui/react": "2.7.8",
|
||||
"@microlink/react-json-view": "^1.26.2",
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@react-router/node": "^7.6.2",
|
||||
"@react-router/serve": "^7.6.2",
|
||||
"@react-router/node": "^7.6.1",
|
||||
"@react-router/serve": "^7.6.1",
|
||||
"@react-types/shared": "^3.29.1",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.3.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"@tanstack/react-query": "^5.80.7",
|
||||
"@vitejs/plugin-react": "^4.5.2",
|
||||
"@stripe/stripe-js": "^7.3.0",
|
||||
"@tanstack/react-query": "^5.77.2",
|
||||
"@vitejs/plugin-react": "^4.4.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"axios": "^1.9.0",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"framer-motion": "^12.17.3",
|
||||
"framer-motion": "^12.14.0",
|
||||
"i18next": "^25.2.1",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-browser-languagedetector": "^8.1.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"isbot": "^5.1.28",
|
||||
"jose": "^6.0.11",
|
||||
"lucide-react": "^0.514.0",
|
||||
"lucide-react": "^0.511.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"posthog-js": "^1.251.0",
|
||||
"posthog-js": "^1.245.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-highlight": "^0.15.0",
|
||||
@@ -42,15 +40,15 @@
|
||||
"react-icons": "^5.5.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^7.6.2",
|
||||
"react-router": "^7.6.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sirv-cli": "^3.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"vite": "^6.3.5",
|
||||
"web-vitals": "^5.0.3",
|
||||
"web-vitals": "^5.0.1",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -84,23 +82,23 @@
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/types": "^7.27.0",
|
||||
"@mswjs/socket.io-binding": "^0.1.1",
|
||||
"@playwright/test": "^1.53.0",
|
||||
"@react-router/dev": "^7.6.2",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@react-router/dev": "^7.6.1",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/eslint-plugin-query": "^5.78.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/node": "^24.0.1",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/node": "^22.15.21",
|
||||
"@types/react": "^19.1.5",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"@vitest/coverage-v8": "^3.2.3",
|
||||
"@vitest/coverage-v8": "^3.1.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.57.0",
|
||||
@@ -115,11 +113,12 @@
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
"husky": "^9.1.7",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "^16.1.0",
|
||||
"lint-staged": "^16.0.0",
|
||||
"msw": "^2.6.6",
|
||||
"postcss": "^8.5.2",
|
||||
"prettier": "^3.5.3",
|
||||
"stripe": "^18.2.1",
|
||||
"tailwindcss": "^4.1.8",
|
||||
"stripe": "^18.1.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.8.3",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,23 +5,24 @@
|
||||
* Mock Service Worker.
|
||||
* @see https://github.com/mswjs/msw
|
||||
* - Please do NOT modify this file.
|
||||
* - Please do NOT serve this file on production.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.10.2'
|
||||
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
|
||||
const PACKAGE_VERSION = '2.8.4'
|
||||
const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
addEventListener('install', function () {
|
||||
self.addEventListener('install', function () {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
addEventListener('activate', function (event) {
|
||||
self.addEventListener('activate', function (event) {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
addEventListener('message', async function (event) {
|
||||
const clientId = Reflect.get(event.source || {}, 'id')
|
||||
self.addEventListener('message', async function (event) {
|
||||
const clientId = event.source.id
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return
|
||||
@@ -93,18 +94,17 @@ addEventListener('message', async function (event) {
|
||||
}
|
||||
})
|
||||
|
||||
addEventListener('fetch', function (event) {
|
||||
self.addEventListener('fetch', function (event) {
|
||||
const { request } = event
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (event.request.mode === 'navigate') {
|
||||
if (request.mode === 'navigate') {
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (
|
||||
event.request.cache === 'only-if-cached' &&
|
||||
event.request.mode !== 'same-origin'
|
||||
) {
|
||||
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,62 +115,48 @@ addEventListener('fetch', function (event) {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate unique request ID.
|
||||
const requestId = crypto.randomUUID()
|
||||
event.respondWith(handleRequest(event, requestId))
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {string} requestId
|
||||
*/
|
||||
async function handleRequest(event, requestId) {
|
||||
const client = await resolveMainClient(event)
|
||||
const requestCloneForEvents = event.request.clone()
|
||||
const response = await getResponse(event, client, requestId)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
||||
;(async function () {
|
||||
const responseClone = response.clone()
|
||||
|
||||
// Clone the response so both the client and the library could consume it.
|
||||
const responseClone = response.clone()
|
||||
|
||||
sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
||||
request: {
|
||||
id: requestId,
|
||||
...serializedRequest,
|
||||
},
|
||||
response: {
|
||||
sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
requestId,
|
||||
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
||||
type: responseClone.type,
|
||||
status: responseClone.status,
|
||||
statusText: responseClone.statusText,
|
||||
headers: Object.fromEntries(responseClone.headers.entries()),
|
||||
body: responseClone.body,
|
||||
headers: Object.fromEntries(responseClone.headers.entries()),
|
||||
},
|
||||
},
|
||||
},
|
||||
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
||||
)
|
||||
[responseClone.body],
|
||||
)
|
||||
})()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the main client for the given event.
|
||||
* Client that issues a request doesn't necessarily equal the client
|
||||
* that registered the worker. It's with the latter the worker should
|
||||
* communicate with during the response resolving phase.
|
||||
* @param {FetchEvent} event
|
||||
* @returns {Promise<Client | undefined>}
|
||||
*/
|
||||
// Resolve the main client for the given event.
|
||||
// Client that issues a request doesn't necessarily equal the client
|
||||
// that registered the worker. It's with the latter the worker should
|
||||
// communicate with during the response resolving phase.
|
||||
async function resolveMainClient(event) {
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
@@ -198,16 +184,12 @@ async function resolveMainClient(event) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {Client | undefined} client
|
||||
* @param {string} requestId
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async function getResponse(event, client, requestId) {
|
||||
const { request } = event
|
||||
|
||||
// Clone the request because it might've been already used
|
||||
// (i.e. its body has been read and sent to the client).
|
||||
const requestClone = event.request.clone()
|
||||
const requestClone = request.clone()
|
||||
|
||||
function passthrough() {
|
||||
// Cast the request headers to a new Headers instance
|
||||
@@ -248,17 +230,29 @@ async function getResponse(event, client, requestId) {
|
||||
}
|
||||
|
||||
// Notify the client that a request has been intercepted.
|
||||
const serializedRequest = await serializeRequest(event.request)
|
||||
const requestBuffer = await request.arrayBuffer()
|
||||
const clientMessage = await sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
...serializedRequest,
|
||||
url: request.url,
|
||||
mode: request.mode,
|
||||
method: request.method,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
cache: request.cache,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body: requestBuffer,
|
||||
keepalive: request.keepalive,
|
||||
},
|
||||
},
|
||||
[serializedRequest.body],
|
||||
[requestBuffer],
|
||||
)
|
||||
|
||||
switch (clientMessage.type) {
|
||||
@@ -274,12 +268,6 @@ async function getResponse(event, client, requestId) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Client} client
|
||||
* @param {any} message
|
||||
* @param {Array<Transferable>} transferrables
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
function sendToClient(client, message, transferrables = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel()
|
||||
@@ -292,18 +280,14 @@ function sendToClient(client, message, transferrables = []) {
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(message, [
|
||||
channel.port2,
|
||||
...transferrables.filter(Boolean),
|
||||
])
|
||||
client.postMessage(
|
||||
message,
|
||||
[channel.port2].concat(transferrables.filter(Boolean)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @returns {Response}
|
||||
*/
|
||||
function respondWithMock(response) {
|
||||
async function respondWithMock(response) {
|
||||
// Setting response status code to 0 is a no-op.
|
||||
// However, when responding with a "Response.error()", the produced Response
|
||||
// instance will have status code set to 0. Since it's not possible to create
|
||||
@@ -321,24 +305,3 @@ function respondWithMock(response) {
|
||||
|
||||
return mockedResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} request
|
||||
*/
|
||||
async function serializeRequest(request) {
|
||||
return {
|
||||
url: request.url,
|
||||
mode: request.mode,
|
||||
method: request.method,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
cache: request.cache,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body: await request.arrayBuffer(),
|
||||
keepalive: request.keepalive,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,9 +117,6 @@ const EXCLUDED_TECHNICAL_STRINGS = [
|
||||
"edit-secret-form", // Test ID for secret form
|
||||
"search-api-key-input", // Input name for search API key
|
||||
"noopener,noreferrer", // Options for window.open
|
||||
"STATUS$READY",
|
||||
"STATUS$STOPPED",
|
||||
"STATUS$ERROR",
|
||||
];
|
||||
|
||||
function isExcludedTechnicalString(str) {
|
||||
|
||||
@@ -11,8 +11,6 @@ import {
|
||||
GetTrajectoryResponse,
|
||||
GitChangeDiff,
|
||||
GitChange,
|
||||
GetMicroagentsResponse,
|
||||
GetMicroagentPromptResponse,
|
||||
} from "./open-hands.types";
|
||||
import { openHands } from "./open-hands-axios";
|
||||
import { ApiSettings, PostApiSettings, Provider } from "#/types/settings";
|
||||
@@ -395,35 +393,6 @@ class OpenHands {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available microagents associated with a conversation
|
||||
* @param conversationId The ID of the conversation
|
||||
* @returns The available microagents associated with the conversation
|
||||
*/
|
||||
static async getMicroagents(
|
||||
conversationId: string,
|
||||
): Promise<GetMicroagentsResponse> {
|
||||
const url = `${this.getConversationUrl(conversationId)}/microagents`;
|
||||
const { data } = await openHands.get<GetMicroagentsResponse>(url, {
|
||||
headers: this.getConversationHeaders(),
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
static async getMicroagentPrompt(
|
||||
conversationId: string,
|
||||
eventId: number,
|
||||
): Promise<string> {
|
||||
const { data } = await openHands.get<GetMicroagentPromptResponse>(
|
||||
`/api/conversations/${conversationId}/remember_prompt`,
|
||||
{
|
||||
params: { event_id: eventId },
|
||||
},
|
||||
);
|
||||
|
||||
return data.prompt;
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenHands;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ConversationStatus } from "#/types/conversation-status";
|
||||
import { RuntimeStatus } from "#/types/runtime-status";
|
||||
import { ProjectStatus } from "#/components/features/conversation-panel/conversation-state-indicator";
|
||||
|
||||
export interface ErrorResponse {
|
||||
error: string;
|
||||
@@ -81,8 +80,7 @@ export interface Conversation {
|
||||
git_provider: string | null;
|
||||
last_updated_at: string;
|
||||
created_at: string;
|
||||
status: ConversationStatus;
|
||||
runtime_status: RuntimeStatus | null;
|
||||
status: ProjectStatus;
|
||||
trigger?: ConversationTrigger;
|
||||
url: string | null;
|
||||
session_api_key: string | null;
|
||||
@@ -104,24 +102,3 @@ export interface GitChangeDiff {
|
||||
modified: string;
|
||||
original: string;
|
||||
}
|
||||
|
||||
export interface InputMetadata {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Microagent {
|
||||
name: string;
|
||||
type: "repo" | "knowledge";
|
||||
content: string;
|
||||
triggers: string[];
|
||||
}
|
||||
|
||||
export interface GetMicroagentsResponse {
|
||||
microagents: Microagent[];
|
||||
}
|
||||
|
||||
export interface GetMicroagentPromptResponse {
|
||||
status: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Layer_1" x="0" y="0" style="enable-background:new 0 0 468 222.5" version="1.1" viewBox="0 0 468 222.5">
|
||||
<style>
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#635bff}
|
||||
</style>
|
||||
<path d="M414 113.4c0-25.6-12.4-45.8-36.1-45.8-23.8 0-38.2 20.2-38.2 45.6 0 30.1 17 45.3 41.4 45.3 11.9 0 20.9-2.7 27.7-6.5v-20c-6.8 3.4-14.6 5.5-24.5 5.5-9.7 0-18.3-3.4-19.4-15.2h48.9c0-1.3.2-6.5.2-8.9zm-49.4-9.5c0-11.3 6.9-16 13.2-16 6.1 0 12.6 4.7 12.6 16h-25.8zM301.1 67.6c-9.8 0-16.1 4.6-19.6 7.8l-1.3-6.2h-22v116.6l25-5.3.1-28.3c3.6 2.6 8.9 6.3 17.7 6.3 17.9 0 34.2-14.4 34.2-46.1-.1-29-16.6-44.8-34.1-44.8zm-6 68.9c-5.9 0-9.4-2.1-11.8-4.7l-.1-37.1c2.6-2.9 6.2-4.9 11.9-4.9 9.1 0 15.4 10.2 15.4 23.3 0 13.4-6.2 23.4-15.4 23.4zM223.8 61.7l25.1-5.4V36l-25.1 5.3zM223.8 69.3h25.1v87.5h-25.1zM196.9 76.7l-1.6-7.4h-21.6v87.5h25V97.5c5.9-7.7 15.9-6.3 19-5.2v-23c-3.2-1.2-14.9-3.4-20.8 7.4zM146.9 47.6l-24.4 5.2-.1 80.1c0 14.8 11.1 25.7 25.9 25.7 8.2 0 14.2-1.5 17.5-3.3V135c-3.2 1.3-19 5.9-19-8.9V90.6h19V69.3h-19l.1-21.7zM79.3 94.7c0-3.9 3.2-5.4 8.5-5.4 7.6 0 17.2 2.3 24.8 6.4V72.2c-8.3-3.3-16.5-4.6-24.8-4.6C67.5 67.6 54 78.2 54 95.9c0 27.6 38 23.2 38 35.1 0 4.6-4 6.1-9.6 6.1-8.3 0-18.9-3.4-27.3-8v23.8c9.3 4 18.7 5.7 27.3 5.7 20.8 0 35.1-10.3 35.1-28.2-.1-29.8-38.2-24.5-38.2-35.7z" class="st0"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
68
frontend/src/components/agent-status-map.constant.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { AgentState } from "#/types/agent-state";
|
||||
|
||||
export enum IndicatorColor {
|
||||
BLUE = "bg-blue-500",
|
||||
GREEN = "bg-green-500",
|
||||
ORANGE = "bg-orange-500",
|
||||
YELLOW = "bg-yellow-500",
|
||||
RED = "bg-red-500",
|
||||
DARK_ORANGE = "bg-orange-800",
|
||||
}
|
||||
|
||||
export const AGENT_STATUS_MAP: {
|
||||
[k: string]: { message: string; indicator: IndicatorColor };
|
||||
} = {
|
||||
[AgentState.INIT]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_INIT_MESSAGE,
|
||||
indicator: IndicatorColor.BLUE,
|
||||
},
|
||||
[AgentState.RUNNING]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_RUNNING_MESSAGE,
|
||||
indicator: IndicatorColor.GREEN,
|
||||
},
|
||||
[AgentState.AWAITING_USER_INPUT]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_AWAITING_USER_INPUT_MESSAGE,
|
||||
indicator: IndicatorColor.BLUE,
|
||||
},
|
||||
[AgentState.PAUSED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_PAUSED_MESSAGE,
|
||||
indicator: IndicatorColor.YELLOW,
|
||||
},
|
||||
[AgentState.LOADING]: {
|
||||
message: I18nKey.CHAT_INTERFACE$INITIALIZING_AGENT_LOADING_MESSAGE,
|
||||
indicator: IndicatorColor.DARK_ORANGE,
|
||||
},
|
||||
[AgentState.STOPPED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_STOPPED_MESSAGE,
|
||||
indicator: IndicatorColor.RED,
|
||||
},
|
||||
[AgentState.FINISHED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_FINISHED_MESSAGE,
|
||||
indicator: IndicatorColor.GREEN,
|
||||
},
|
||||
[AgentState.REJECTED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_REJECTED_MESSAGE,
|
||||
indicator: IndicatorColor.YELLOW,
|
||||
},
|
||||
[AgentState.ERROR]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_ERROR_MESSAGE,
|
||||
indicator: IndicatorColor.RED,
|
||||
},
|
||||
[AgentState.AWAITING_USER_CONFIRMATION]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_AWAITING_USER_CONFIRMATION_MESSAGE,
|
||||
indicator: IndicatorColor.ORANGE,
|
||||
},
|
||||
[AgentState.USER_CONFIRMED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_ACTION_USER_CONFIRMED_MESSAGE,
|
||||
indicator: IndicatorColor.GREEN,
|
||||
},
|
||||
[AgentState.USER_REJECTED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_ACTION_USER_REJECTED_MESSAGE,
|
||||
indicator: IndicatorColor.RED,
|
||||
},
|
||||
[AgentState.RATE_LIMITED]: {
|
||||
message: I18nKey.CHAT_INTERFACE$AGENT_RATE_LIMITED_MESSAGE,
|
||||
indicator: IndicatorColor.YELLOW,
|
||||
},
|
||||
};
|
||||
@@ -132,7 +132,7 @@ export function ChatInput({
|
||||
maxRows={maxRows}
|
||||
data-dragging-over={isDraggingOver}
|
||||
className={cn(
|
||||
"grow text-sm self-center placeholder:text-neutral-400 text-white resize-none outline-hidden ring-0",
|
||||
"grow text-sm self-center placeholder:text-neutral-400 text-white resize-none outline-none ring-0",
|
||||
"transition-all duration-200 ease-in-out",
|
||||
isDraggingOver
|
||||
? "bg-neutral-600/50 rounded-lg px-2"
|
||||
|
||||
@@ -114,7 +114,7 @@ export function ExpandableMessage({
|
||||
{t(I18nKey.STATUS$ERROR_LLM_OUT_OF_CREDITS)}
|
||||
</div>
|
||||
<Link
|
||||
className="mt-2 mb-2 w-full h-10 rounded-sm flex items-center justify-center gap-2 bg-primary text-[#0D0F11]"
|
||||
className="mt-2 mb-2 w-full h-10 rounded flex items-center justify-center gap-2 bg-primary text-[#0D0F11]"
|
||||
to="/settings/billing"
|
||||
>
|
||||
{t(I18nKey.BILLING$CLICK_TO_TOP_UP)}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { showErrorToast } from "#/utils/error-handler";
|
||||
import { RootState } from "#/store";
|
||||
import { AgentState } from "#/types/agent-state";
|
||||
import { useWsClient } from "#/context/ws-client-provider";
|
||||
import {
|
||||
AGENT_STATUS_MAP,
|
||||
IndicatorColor,
|
||||
} from "../../agent-status-map.constant";
|
||||
import {
|
||||
useWsClient,
|
||||
WsClientProviderStatus,
|
||||
} from "#/context/ws-client-provider";
|
||||
import { useNotification } from "#/hooks/useNotification";
|
||||
import { browserTab } from "#/utils/browser-tab";
|
||||
import { useActiveConversation } from "#/hooks/query/use-active-conversation";
|
||||
import { getIndicatorColor, getStatusCode } from "#/utils/status";
|
||||
|
||||
const notificationStates = [
|
||||
AgentState.AWAITING_USER_INPUT,
|
||||
@@ -20,60 +27,38 @@ export function AgentStatusBar() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
||||
const { curStatusMessage } = useSelector((state: RootState) => state.status);
|
||||
const { webSocketStatus } = useWsClient();
|
||||
const { data: conversation } = useActiveConversation();
|
||||
const indicatorColor = getIndicatorColor(
|
||||
webSocketStatus,
|
||||
conversation?.status || null,
|
||||
conversation?.runtime_status || null,
|
||||
curAgentState,
|
||||
);
|
||||
const statusCode = getStatusCode(
|
||||
curStatusMessage,
|
||||
webSocketStatus,
|
||||
conversation?.status || null,
|
||||
conversation?.runtime_status || null,
|
||||
curAgentState,
|
||||
);
|
||||
const { status } = useWsClient();
|
||||
const { notify } = useNotification();
|
||||
const { data: conversation } = useActiveConversation();
|
||||
|
||||
// Show error toast if required
|
||||
React.useEffect(() => {
|
||||
if (curStatusMessage?.type !== "error") {
|
||||
return;
|
||||
}
|
||||
const [statusMessage, setStatusMessage] = React.useState<string>("");
|
||||
|
||||
const updateStatusMessage = () => {
|
||||
let message = curStatusMessage.message || "";
|
||||
if (curStatusMessage?.id) {
|
||||
const id = curStatusMessage.id.trim();
|
||||
if (id === "STATUS$READY") {
|
||||
message = "awaiting_user_input";
|
||||
}
|
||||
if (i18n.exists(id)) {
|
||||
message = t(curStatusMessage.id.trim()) || message;
|
||||
}
|
||||
}
|
||||
showErrorToast({
|
||||
message,
|
||||
source: "agent-status",
|
||||
metadata: { ...curStatusMessage },
|
||||
});
|
||||
}, [curStatusMessage.id]);
|
||||
|
||||
// Handle notify
|
||||
React.useEffect(() => {
|
||||
if (notificationStates.includes(curAgentState)) {
|
||||
const message = t(statusCode);
|
||||
notify(message, {
|
||||
body: t(`Agent state changed to ${curAgentState}`),
|
||||
playSound: true,
|
||||
if (curStatusMessage?.type === "error") {
|
||||
showErrorToast({
|
||||
message,
|
||||
source: "agent-status",
|
||||
metadata: { ...curStatusMessage },
|
||||
});
|
||||
|
||||
// Update browser tab if window exists and is not focused
|
||||
if (typeof document !== "undefined" && !document.hasFocus()) {
|
||||
browserTab.startNotification(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, [curAgentState, statusCode]);
|
||||
if (message.trim()) {
|
||||
setStatusMessage(message);
|
||||
} else {
|
||||
setStatusMessage(AGENT_STATUS_MAP[curAgentState].message);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
updateStatusMessage();
|
||||
}, [curStatusMessage.id]);
|
||||
|
||||
// Handle window focus/blur
|
||||
React.useEffect(() => {
|
||||
@@ -90,13 +75,42 @@ export function AgentStatusBar() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [indicatorColor, setIndicatorColor] = React.useState<string>(
|
||||
AGENT_STATUS_MAP[curAgentState].indicator,
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (conversation?.status === "STARTING") {
|
||||
setStatusMessage(t(I18nKey.STATUS$STARTING_RUNTIME));
|
||||
setIndicatorColor(IndicatorColor.RED);
|
||||
} else if (status === WsClientProviderStatus.DISCONNECTED) {
|
||||
setStatusMessage(t(I18nKey.STATUS$WEBSOCKET_CLOSED));
|
||||
setIndicatorColor(IndicatorColor.RED);
|
||||
} else {
|
||||
setStatusMessage(AGENT_STATUS_MAP[curAgentState].message);
|
||||
setIndicatorColor(AGENT_STATUS_MAP[curAgentState].indicator);
|
||||
if (notificationStates.includes(curAgentState)) {
|
||||
const message = t(AGENT_STATUS_MAP[curAgentState].message);
|
||||
notify(t(AGENT_STATUS_MAP[curAgentState].message), {
|
||||
body: t(`Agent state changed to ${curAgentState}`),
|
||||
playSound: true,
|
||||
});
|
||||
|
||||
// Update browser tab if window exists and is not focused
|
||||
if (typeof document !== "undefined" && !document.hasFocus()) {
|
||||
browserTab.startNotification(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [curAgentState, status, notify, t, conversation?.status]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="flex items-center bg-base-secondary px-2 py-1 text-gray-400 rounded-[100px] text-sm gap-[6px]">
|
||||
<div
|
||||
className={`w-2 h-2 rounded-full animate-pulse ${indicatorColor}`}
|
||||
/>
|
||||
<span className="text-sm text-stone-400">{t(statusCode)}</span>
|
||||
<span className="text-sm text-stone-400">{t(statusMessage)}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ export function Controls({ setSecurityOpen, showSecurityLock }: ControlsProps) {
|
||||
title={conversation?.title ?? ""}
|
||||
lastUpdatedAt={conversation?.created_at ?? ""}
|
||||
selectedRepository={conversation?.selected_repository ?? null}
|
||||
conversationStatus={conversation?.status}
|
||||
status={conversation?.status}
|
||||
conversationId={conversation?.conversation_id}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useClickOutsideElement } from "#/hooks/use-click-outside-element";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { ContextMenu } from "../context-menu/context-menu";
|
||||
import { ContextMenuListItem } from "../context-menu/context-menu-list-item";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
interface ConversationCardContextMenuProps {
|
||||
onClose: () => void;
|
||||
@@ -11,7 +9,6 @@ interface ConversationCardContextMenuProps {
|
||||
onEdit?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onDisplayCost?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onShowAgentTools?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onShowMicroagents?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onDownloadViaVSCode?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
position?: "top" | "bottom";
|
||||
}
|
||||
@@ -22,11 +19,9 @@ export function ConversationCardContextMenu({
|
||||
onEdit,
|
||||
onDisplayCost,
|
||||
onShowAgentTools,
|
||||
onShowMicroagents,
|
||||
onDownloadViaVSCode,
|
||||
position = "bottom",
|
||||
}: ConversationCardContextMenuProps) {
|
||||
const { t } = useTranslation();
|
||||
const ref = useClickOutsideElement<HTMLUListElement>(onClose);
|
||||
|
||||
return (
|
||||
@@ -73,14 +68,6 @@ export function ConversationCardContextMenu({
|
||||
Show Agent Tools & Metadata
|
||||
</ContextMenuListItem>
|
||||
)}
|
||||
{onShowMicroagents && (
|
||||
<ContextMenuListItem
|
||||
testId="show-microagents-button"
|
||||
onClick={onShowMicroagents}
|
||||
>
|
||||
{t(I18nKey.CONVERSATION$SHOW_MICROAGENTS)}
|
||||
</ContextMenuListItem>
|
||||
)}
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ import posthog from "posthog-js";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatTimeDelta } from "#/utils/format-time-delta";
|
||||
import { ConversationRepoLink } from "./conversation-repo-link";
|
||||
import { ConversationStateIndicator } from "./conversation-state-indicator";
|
||||
import {
|
||||
ProjectStatus,
|
||||
ConversationStateIndicator,
|
||||
} from "./conversation-state-indicator";
|
||||
import { EllipsisButton } from "./ellipsis-button";
|
||||
import { ConversationCardContextMenu } from "./conversation-card-context-menu";
|
||||
import { SystemMessageModal } from "./system-message-modal";
|
||||
import { MicroagentsModal } from "./microagents-modal";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { BaseModal } from "../../shared/modals/base-modal/base-modal";
|
||||
import { RootState } from "#/store";
|
||||
@@ -17,7 +19,6 @@ import { transformVSCodeUrl } from "#/utils/vscode-url-helper";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { useWsClient } from "#/context/ws-client-provider";
|
||||
import { isSystemMessage } from "#/types/core/guards";
|
||||
import { ConversationStatus } from "#/types/conversation-status";
|
||||
|
||||
interface ConversationCardProps {
|
||||
onClick?: () => void;
|
||||
@@ -29,7 +30,7 @@ interface ConversationCardProps {
|
||||
selectedRepository: string | null;
|
||||
lastUpdatedAt: string; // ISO 8601
|
||||
createdAt?: string; // ISO 8601
|
||||
conversationStatus?: ConversationStatus;
|
||||
status?: ProjectStatus;
|
||||
variant?: "compact" | "default";
|
||||
conversationId?: string; // Optional conversation ID for VS Code URL
|
||||
}
|
||||
@@ -48,7 +49,7 @@ export function ConversationCard({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
lastUpdatedAt,
|
||||
createdAt,
|
||||
conversationStatus = "STOPPED",
|
||||
status = "STOPPED",
|
||||
variant = "default",
|
||||
conversationId,
|
||||
}: ConversationCardProps) {
|
||||
@@ -58,8 +59,6 @@ export function ConversationCard({
|
||||
const [titleMode, setTitleMode] = React.useState<"view" | "edit">("view");
|
||||
const [metricsModalVisible, setMetricsModalVisible] = React.useState(false);
|
||||
const [systemModalVisible, setSystemModalVisible] = React.useState(false);
|
||||
const [microagentsModalVisible, setMicroagentsModalVisible] =
|
||||
React.useState(false);
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const systemMessage = parsedEvents.find(isSystemMessage);
|
||||
@@ -143,13 +142,6 @@ export function ConversationCard({
|
||||
setSystemModalVisible(true);
|
||||
};
|
||||
|
||||
const handleShowMicroagents = (
|
||||
event: React.MouseEvent<HTMLButtonElement>,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
setMicroagentsModalVisible(true);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (titleMode === "edit") {
|
||||
inputRef.current?.focus();
|
||||
@@ -204,9 +196,7 @@ export function ConversationCard({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<ConversationStateIndicator
|
||||
conversationStatus={conversationStatus}
|
||||
/>
|
||||
<ConversationStateIndicator status={status} />
|
||||
{hasContextMenu && (
|
||||
<div className="pl-2">
|
||||
<EllipsisButton
|
||||
@@ -235,11 +225,6 @@ export function ConversationCard({
|
||||
? handleShowAgentTools
|
||||
: undefined
|
||||
}
|
||||
onShowMicroagents={
|
||||
showOptions && conversationId
|
||||
? handleShowMicroagents
|
||||
: undefined
|
||||
}
|
||||
position={variant === "compact" ? "top" : "bottom"}
|
||||
/>
|
||||
)}
|
||||
@@ -382,13 +367,6 @@ export function ConversationCard({
|
||||
onClose={() => setSystemModalVisible(false)}
|
||||
systemMessage={systemMessage ? systemMessage.args : null}
|
||||
/>
|
||||
|
||||
{microagentsModalVisible && (
|
||||
<MicroagentsModal
|
||||
onClose={() => setMicroagentsModalVisible(false)}
|
||||
conversationId={conversationId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
|
||||
selectedRepository={project.selected_repository}
|
||||
lastUpdatedAt={project.last_updated_at}
|
||||
createdAt={project.created_at}
|
||||
conversationStatus={project.status}
|
||||
status={project.status}
|
||||
conversationId={project.conversation_id}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import { ConversationStatus } from "#/types/conversation-status";
|
||||
import ColdIcon from "./state-indicators/cold.svg?react";
|
||||
import RunningIcon from "./state-indicators/running.svg?react";
|
||||
import StartingIcon from "./state-indicators/starting.svg?react";
|
||||
import StoppedIcon from "./state-indicators/stopped.svg?react";
|
||||
|
||||
type SVGIcon = React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||
export type ProjectStatus = "RUNNING" | "STOPPED" | "STARTING";
|
||||
|
||||
const CONVERSATION_STATUS_INDICATORS: Record<ConversationStatus, SVGIcon> = {
|
||||
STOPPED: StoppedIcon,
|
||||
const INDICATORS: Record<ProjectStatus, SVGIcon> = {
|
||||
STOPPED: ColdIcon,
|
||||
RUNNING: RunningIcon,
|
||||
STARTING: StartingIcon,
|
||||
STARTING: ColdIcon,
|
||||
};
|
||||
|
||||
interface ConversationStateIndicatorProps {
|
||||
conversationStatus: ConversationStatus;
|
||||
status: ProjectStatus;
|
||||
}
|
||||
|
||||
export function ConversationStateIndicator({
|
||||
conversationStatus,
|
||||
status,
|
||||
}: ConversationStateIndicatorProps) {
|
||||
const StateIcon = CONVERSATION_STATUS_INDICATORS[conversationStatus];
|
||||
const StateIcon = INDICATORS[status];
|
||||
|
||||
return (
|
||||
<div data-testid={`${conversationStatus}-indicator`}>
|
||||
<div data-testid={`${status}-indicator`}>
|
||||
<StateIcon />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { BaseModalTitle } 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 { I18nKey } from "#/i18n/declaration";
|
||||
import { useConversationMicroagents } from "#/hooks/query/use-conversation-microagents";
|
||||
|
||||
interface MicroagentsModalProps {
|
||||
onClose: () => void;
|
||||
conversationId: string | undefined;
|
||||
}
|
||||
|
||||
export function MicroagentsModal({
|
||||
onClose,
|
||||
conversationId,
|
||||
}: MicroagentsModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [expandedAgents, setExpandedAgents] = useState<Record<string, boolean>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const {
|
||||
data: microagents,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useConversationMicroagents({
|
||||
conversationId,
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const toggleAgent = (agentName: string) => {
|
||||
setExpandedAgents((prev) => ({
|
||||
...prev,
|
||||
[agentName]: !prev[agentName],
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalBackdrop onClose={onClose}>
|
||||
<ModalBody
|
||||
width="medium"
|
||||
className="max-h-[80vh] flex flex-col items-start"
|
||||
testID="microagents-modal"
|
||||
>
|
||||
<div className="flex flex-col gap-6 w-full">
|
||||
<BaseModalTitle title={t(I18nKey.MICROAGENTS_MODAL$TITLE)} />
|
||||
</div>
|
||||
|
||||
<div className="w-full h-[60vh] overflow-auto rounded-md">
|
||||
{isLoading && (
|
||||
<div className="flex justify-center items-center py-8">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-primary" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading &&
|
||||
(isError || !microagents || microagents.length === 0) && (
|
||||
<div className="flex items-center justify-center h-full p-4">
|
||||
<p className="text-gray-400">
|
||||
{isError
|
||||
? t(I18nKey.MICROAGENTS_MODAL$FETCH_ERROR)
|
||||
: t(I18nKey.CONVERSATION$NO_MICROAGENTS)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && microagents && microagents.length > 0 && (
|
||||
<div className="p-2 space-y-3">
|
||||
{microagents.map((agent) => {
|
||||
const isExpanded = expandedAgents[agent.name] || false;
|
||||
|
||||
return (
|
||||
<div key={agent.name} className="rounded-md overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleAgent(agent.name)}
|
||||
className="w-full py-3 px-2 text-left flex items-center justify-between hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<h3 className="font-bold text-gray-100">
|
||||
{agent.name}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<span className="px-2 py-1 text-xs rounded-full bg-gray-800 mr-2">
|
||||
{agent.type === "repo" ? "Repository" : "Knowledge"}
|
||||
</span>
|
||||
<span className="text-gray-300">
|
||||
{isExpanded ? (
|
||||
<ChevronDown size={18} />
|
||||
) : (
|
||||
<ChevronRight size={18} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="px-2 pb-3 pt-1">
|
||||
{agent.triggers && agent.triggers.length > 0 && (
|
||||
<div className="mt-2 mb-3">
|
||||
<h4 className="text-sm font-semibold text-gray-300 mb-2">
|
||||
{t(I18nKey.MICROAGENTS_MODAL$TRIGGERS)}
|
||||
</h4>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{agent.triggers.map((trigger) => (
|
||||
<span
|
||||
key={trigger}
|
||||
className="px-2 py-1 text-xs rounded-full bg-blue-900"
|
||||
>
|
||||
{trigger}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-2">
|
||||
<h4 className="text-sm font-semibold text-gray-300 mb-2">
|
||||
{t(I18nKey.MICROAGENTS_MODAL$CONTENT)}
|
||||
</h4>
|
||||
<div className="text-sm mt-2 p-3 bg-gray-900 rounded-md overflow-auto text-gray-300 max-h-[400px] shadow-inner">
|
||||
<pre className="whitespace-pre-wrap font-mono text-sm leading-relaxed">
|
||||
{agent.content ||
|
||||
t(I18nKey.MICROAGENTS_MODAL$NO_CONTENT)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ModalBody>
|
||||
</ModalBackdrop>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 904 B After Width: | Height: | Size: 904 B |
|
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 16.8599C13.4183 16.8599 17 13.2781 17 8.85986C17 4.44159 13.4183 0.859863 9 0.859863C4.58172 0.859863 1 4.44159 1 8.85986C1 13.2781 4.58172 16.8599 9 16.8599Z" fill="#779FD4"/>
|
||||
<path d="M4.61035 8.43014L7.86035 12.0301L13.3904 6.64014" stroke="#231F20" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 433 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.76039 6.99002C8.478 6.99002 9.87039 5.59763 9.87039 3.88002C9.87039 2.16241 8.478 0.77002 6.76039 0.77002C5.04279 0.77002 3.65039 2.16241 3.65039 3.88002C3.65039 5.59763 5.04279 6.99002 6.76039 6.99002Z" fill="#FFE165"/>
|
||||
<path d="M1.0802 17.0799C1.0802 17.0799 0.610196 11.5499 3.0102 9.67992C4.7902 8.29992 7.3302 9.44992 9.7802 7.95992C11.5802 6.86992 13.6102 4.10992 14.5202 2.49992C14.9302 1.77992 15.9102 1.62992 16.6102 2.05992C17.3802 2.51992 17.6102 3.53992 17.1102 4.28992C16.2302 5.58992 14.1802 8.85992 13.1202 10.3699C10.7602 13.7599 11.4302 17.0799 11.4302 17.0799H1.0702H1.0802Z" fill="#FFE165"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 726 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.87012 2.08984C9.87012 1.53756 9.4224 1.08984 8.87012 1.08984C8.31783 1.08984 7.87012 1.53756 7.87012 2.08984V8.08984C7.87012 8.64213 8.31783 9.08984 8.87012 9.08984C9.4224 9.08984 9.87012 8.64213 9.87012 8.08984V2.08984Z" fill="#60BB46"/>
|
||||
<path d="M10.8702 2.50988V2.64988C10.8702 3.01988 11.0702 3.36988 11.4102 3.51988C13.6802 4.51988 15.2202 6.88988 14.9702 9.56988C14.7002 12.5599 12.1002 14.9599 9.09021 15.0099C5.74021 15.0599 2.99021 12.3499 2.99021 9.00988C2.99021 6.65988 4.35021 4.62988 6.31021 3.64988C6.64021 3.48988 6.86021 3.16988 6.86021 2.80988V2.63988C6.86021 1.94988 6.14021 1.51988 5.51021 1.81988C2.42021 3.30988 0.430214 6.71988 1.12021 10.5199C1.69021 13.6799 4.22021 16.2499 7.37021 16.8699C12.4902 17.8699 16.9802 13.9699 16.9802 9.02988C16.9802 5.71988 14.9702 2.88988 12.1002 1.66988C11.5102 1.41988 10.8502 1.88988 10.8502 2.52988L10.8702 2.50988Z" fill="#60BB46"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1008 B |
@@ -101,7 +101,7 @@ export function FeedbackForm({ onClose, polarity }: FeedbackFormProps) {
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder={t(I18nKey.FEEDBACK$EMAIL_PLACEHOLDER)}
|
||||
className="bg-[#27272A] px-3 py-[10px] rounded-sm"
|
||||
className="bg-[#27272A] px-3 py-[10px] rounded"
|
||||
/>
|
||||
</label>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ export function BranchErrorState() {
|
||||
return (
|
||||
<div
|
||||
data-testid="branch-dropdown-error"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm text-red-500"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded text-red-500"
|
||||
>
|
||||
<span className="text-sm">{t("HOME$FAILED_TO_LOAD_BRANCHES")}</span>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ export function BranchLoadingState() {
|
||||
return (
|
||||
<div
|
||||
data-testid="branch-dropdown-loading"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded"
|
||||
>
|
||||
<Spinner size="sm" />
|
||||
<span className="text-sm">{t("HOME$LOADING_BRANCHES")}</span>
|
||||
|
||||
@@ -6,7 +6,7 @@ export function RepositoryErrorState() {
|
||||
return (
|
||||
<div
|
||||
data-testid="repo-dropdown-error"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm text-red-500"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded text-red-500"
|
||||
>
|
||||
<span className="text-sm">{t("HOME$FAILED_TO_LOAD_REPOSITORIES")}</span>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ export function RepositoryLoadingState() {
|
||||
return (
|
||||
<div
|
||||
data-testid="repo-dropdown-loading"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded"
|
||||
>
|
||||
<Spinner size="sm" />
|
||||
<span className="text-sm">{t("HOME$LOADING_REPOSITORIES")}</span>
|
||||
|
||||
@@ -12,7 +12,7 @@ export function Thumbnail({ src, size = "small" }: ThumbnailProps) {
|
||||
alt=""
|
||||
src={src}
|
||||
className={cn(
|
||||
"rounded-sm object-cover",
|
||||
"rounded object-cover",
|
||||
size === "small" && "w-[62px] h-[62px]",
|
||||
size === "large" && "w-[100px] h-[100px]",
|
||||
)}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { BrandButton } from "../settings/brand-button";
|
||||
import { LoadingSpinner } from "#/components/shared/loading-spinner";
|
||||
import { amountIsValid } from "#/utils/amount-is-valid";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { PoweredByStripeTag } from "./powered-by-stripe-tag";
|
||||
|
||||
export function PaymentForm() {
|
||||
const { t } = useTranslation();
|
||||
@@ -43,7 +42,7 @@ export function PaymentForm() {
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center justify-between w-[680px] bg-[#7F7445] rounded-sm px-3 py-2",
|
||||
"flex items-center justify-between w-[680px] bg-[#7F7445] rounded px-3 py-2",
|
||||
"text-[28px] leading-8 -tracking-[0.02em] font-bold",
|
||||
)}
|
||||
>
|
||||
@@ -80,7 +79,6 @@ export function PaymentForm() {
|
||||
{t(I18nKey.PAYMENT$ADD_CREDIT)}
|
||||
</BrandButton>
|
||||
{isPending && <LoadingSpinner size="small" />}
|
||||
<PoweredByStripeTag />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import stripeLogo from "#/assets/stripe.svg";
|
||||
|
||||
export function PoweredByStripeTag() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="text-medium font-semi-bold">
|
||||
{t(I18nKey.BILLING$POWERED_BY)}
|
||||
</span>
|
||||
<img src={stripeLogo} alt="Stripe" className="h-8" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export function BrandButton({
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
"w-fit p-2 text-sm rounded-sm disabled:opacity-30 disabled:cursor-not-allowed hover:opacity-80",
|
||||
"w-fit p-2 text-sm rounded disabled:opacity-30 disabled:cursor-not-allowed hover:opacity-80",
|
||||
variant === "primary" && "bg-primary text-[#0D0F11]",
|
||||
variant === "secondary" && "border border-primary text-primary",
|
||||
variant === "danger" && "bg-red-600 text-white hover:bg-red-700",
|
||||
|
||||
@@ -69,7 +69,7 @@ export function MCPJsonEditor({ mcpConfig, onChange }: MCPJsonEditorProps) {
|
||||
{t(I18nKey.SETTINGS$MCP_CONFIG_DESCRIPTION)}
|
||||
</div>
|
||||
<textarea
|
||||
className="w-full h-64 p-2 text-sm font-mono bg-base-tertiary rounded-md focus:border-blue-500 focus:outline-hidden"
|
||||
className="w-full h-64 p-2 text-sm font-mono bg-base-tertiary rounded-md focus:border-blue-500 focus:outline-none"
|
||||
value={configText}
|
||||
onChange={handleTextChange}
|
||||
spellCheck="false"
|
||||
|
||||
@@ -158,7 +158,7 @@ export function SecretForm({
|
||||
required
|
||||
className={cn(
|
||||
"resize-none",
|
||||
"bg-tertiary border border-[#717888] rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"bg-tertiary border border-[#717888] rounded p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
rows={8}
|
||||
@@ -177,7 +177,7 @@ export function SecretForm({
|
||||
defaultValue={secretDescription}
|
||||
className={cn(
|
||||
"resize-none",
|
||||
"bg-tertiary border border-[#717888] rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"bg-tertiary border border-[#717888] rounded p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -63,7 +63,7 @@ export function SettingsDropdownInput({
|
||||
inputProps={{
|
||||
classNames: {
|
||||
inputWrapper:
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded-sm p-2 placeholder:italic",
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded p-2 placeholder:italic",
|
||||
},
|
||||
}}
|
||||
defaultFilter={defaultFilter}
|
||||
|
||||
@@ -62,7 +62,7 @@ export function SettingsInput({
|
||||
required={required}
|
||||
pattern={pattern}
|
||||
className={cn(
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,7 @@ export function ReplaySuggestionBox({ onChange }: ReplaySuggestionBoxProps) {
|
||||
htmlFor="import-trajectory"
|
||||
className="w-full flex justify-center"
|
||||
>
|
||||
<span className="border-2 border-dashed border-neutral-600 rounded-sm px-2 py-1 cursor-pointer">
|
||||
<span className="border-2 border-dashed border-neutral-600 rounded px-2 py-1 cursor-pointer">
|
||||
{t(I18nKey.LANDING$UPLOAD_TRAJECTORY)}
|
||||
</span>
|
||||
<input
|
||||
|
||||
@@ -18,7 +18,7 @@ export function EditorActionButton({
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
"text-sm py-0.5 rounded-sm w-20",
|
||||
"text-sm py-0.5 rounded w-20",
|
||||
"hover:bg-tertiary disabled:opacity-50 disabled:cursor-not-allowed",
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -31,7 +31,7 @@ export function ModalButton({
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
variant === "default" && "text-sm font-[500] py-[10px] rounded-sm",
|
||||
variant === "default" && "text-sm font-[500] py-[10px] rounded",
|
||||
variant === "text-like" && "text-xs leading-4 font-normal",
|
||||
icon && "flex items-center justify-center gap-2",
|
||||
disabled && "opacity-50 cursor-not-allowed",
|
||||
|
||||
@@ -36,7 +36,7 @@ export function CustomInput({
|
||||
required={required}
|
||||
defaultValue={defaultValue}
|
||||
type={type}
|
||||
className="bg-[#27272A] text-xs py-[10px] px-3 rounded-sm"
|
||||
className="bg-[#27272A] text-xs py-[10px] px-3 rounded"
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ export function ErrorToast({ id, error }: ErrorToastProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toast.dismiss(id)}
|
||||
className="bg-neutral-500 px-1 rounded-sm h-full"
|
||||
className="bg-neutral-500 px-1 rounded h-full"
|
||||
>
|
||||
{t(I18nKey.ERROR_TOAST$CLOSE_BUTTON_LABEL)}
|
||||
</button>
|
||||
|
||||
@@ -269,19 +269,19 @@ function SecurityInvariant() {
|
||||
<hr className="border-t border-neutral-600 my-2" />
|
||||
<ul className="space-y-2">
|
||||
<div
|
||||
className={`cursor-pointer p-2 rounded-sm ${activeSection === "logs" && "bg-neutral-600"}`}
|
||||
className={`cursor-pointer p-2 rounded ${activeSection === "logs" && "bg-neutral-600"}`}
|
||||
onClick={() => setActiveSection("logs")}
|
||||
>
|
||||
{t(I18nKey.INVARIANT$LOG_LABEL)}
|
||||
</div>
|
||||
<div
|
||||
className={`cursor-pointer p-2 rounded-sm ${activeSection === "policy" && "bg-neutral-600"}`}
|
||||
className={`cursor-pointer p-2 rounded ${activeSection === "policy" && "bg-neutral-600"}`}
|
||||
onClick={() => setActiveSection("policy")}
|
||||
>
|
||||
{t(I18nKey.INVARIANT$POLICY_LABEL)}
|
||||
</div>
|
||||
<div
|
||||
className={`cursor-pointer p-2 rounded-sm ${activeSection === "settings" && "bg-neutral-600"}`}
|
||||
className={`cursor-pointer p-2 rounded ${activeSection === "settings" && "bg-neutral-600"}`}
|
||||
onClick={() => setActiveSection("settings")}
|
||||
>
|
||||
{t(I18nKey.INVARIANT$SETTINGS_LABEL)}
|
||||
|
||||
@@ -92,7 +92,7 @@ export function ModelSelector({
|
||||
inputProps={{
|
||||
classNames: {
|
||||
inputWrapper:
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded-sm p-2 placeholder:italic",
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded p-2 placeholder:italic",
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -142,7 +142,7 @@ export function ModelSelector({
|
||||
inputProps={{
|
||||
classNames: {
|
||||
inputWrapper:
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded-sm p-2 placeholder:italic",
|
||||
"bg-tertiary border border-[#717888] h-10 w-full rounded p-2 placeholder:italic",
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -29,8 +29,6 @@ import {
|
||||
import { useOptimisticUserMessage } from "#/hooks/use-optimistic-user-message";
|
||||
import { useWSErrorMessage } from "#/hooks/use-ws-error-message";
|
||||
|
||||
export type WebSocketStatus = "CONNECTING" | "CONNECTED" | "DISCONNECTED";
|
||||
|
||||
const hasValidMessageProperty = (obj: unknown): obj is { message: string } =>
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
@@ -69,8 +67,14 @@ const isMessageAction = (
|
||||
): event is UserMessageAction | AssistantMessageAction =>
|
||||
isUserMessage(event) || isAssistantMessage(event);
|
||||
|
||||
export enum WsClientProviderStatus {
|
||||
CONNECTED,
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
}
|
||||
|
||||
interface UseWsClient {
|
||||
webSocketStatus: WebSocketStatus;
|
||||
status: WsClientProviderStatus;
|
||||
isLoadingMessages: boolean;
|
||||
events: Record<string, unknown>[];
|
||||
parsedEvents: (OpenHandsAction | OpenHandsObservation)[];
|
||||
@@ -78,7 +82,7 @@ interface UseWsClient {
|
||||
}
|
||||
|
||||
const WsClientContext = React.createContext<UseWsClient>({
|
||||
webSocketStatus: "DISCONNECTED",
|
||||
status: WsClientProviderStatus.DISCONNECTED,
|
||||
isLoadingMessages: true,
|
||||
events: [],
|
||||
parsedEvents: [],
|
||||
@@ -135,8 +139,9 @@ export function WsClientProvider({
|
||||
const { setErrorMessage, removeErrorMessage } = useWSErrorMessage();
|
||||
const queryClient = useQueryClient();
|
||||
const sioRef = React.useRef<Socket | null>(null);
|
||||
const [webSocketStatus, setWebSocketStatus] =
|
||||
React.useState<WebSocketStatus>("DISCONNECTED");
|
||||
const [status, setStatus] = React.useState(
|
||||
WsClientProviderStatus.DISCONNECTED,
|
||||
);
|
||||
const [events, setEvents] = React.useState<Record<string, unknown>[]>([]);
|
||||
const [parsedEvents, setParsedEvents] = React.useState<
|
||||
(OpenHandsAction | OpenHandsObservation)[]
|
||||
@@ -157,7 +162,7 @@ export function WsClientProvider({
|
||||
}
|
||||
|
||||
function handleConnect() {
|
||||
setWebSocketStatus("CONNECTED");
|
||||
setStatus(WsClientProviderStatus.CONNECTED);
|
||||
removeErrorMessage();
|
||||
}
|
||||
|
||||
@@ -256,7 +261,7 @@ export function WsClientProvider({
|
||||
}
|
||||
|
||||
function handleDisconnect(data: unknown) {
|
||||
setWebSocketStatus("DISCONNECTED");
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
const sio = sioRef.current;
|
||||
if (!sio) {
|
||||
return;
|
||||
@@ -270,7 +275,7 @@ export function WsClientProvider({
|
||||
|
||||
function handleError(data: unknown) {
|
||||
// set status
|
||||
setWebSocketStatus("DISCONNECTED");
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
updateStatusWhenErrorMessagePresent(data);
|
||||
|
||||
setErrorMessage(
|
||||
@@ -289,14 +294,17 @@ export function WsClientProvider({
|
||||
// reset events when conversationId changes
|
||||
setEvents([]);
|
||||
setParsedEvents([]);
|
||||
setWebSocketStatus("CONNECTING");
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
}, [conversationId]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!conversationId) {
|
||||
throw new Error("No conversation ID provided");
|
||||
}
|
||||
if (conversation?.status !== "RUNNING" && !conversation?.runtime_status) {
|
||||
if (
|
||||
!conversation ||
|
||||
["STOPPED", "STARTING"].includes(conversation.status)
|
||||
) {
|
||||
return () => undefined; // conversation not yet loaded
|
||||
}
|
||||
|
||||
@@ -306,9 +314,6 @@ export function WsClientProvider({
|
||||
sio.disconnect();
|
||||
}
|
||||
|
||||
// Set initial status...
|
||||
setWebSocketStatus("CONNECTING");
|
||||
|
||||
const lastEvent = lastEventRef.current;
|
||||
const query = {
|
||||
latest_event_id: lastEvent?.id ?? -1,
|
||||
@@ -343,12 +348,7 @@ export function WsClientProvider({
|
||||
sio.off("connect_failed", handleError);
|
||||
sio.off("disconnect", handleDisconnect);
|
||||
};
|
||||
}, [
|
||||
conversationId,
|
||||
conversation?.url,
|
||||
conversation?.status,
|
||||
conversation?.runtime_status,
|
||||
]);
|
||||
}, [conversationId, conversation?.url, conversation?.status]);
|
||||
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
@@ -363,18 +363,13 @@ export function WsClientProvider({
|
||||
|
||||
const value = React.useMemo<UseWsClient>(
|
||||
() => ({
|
||||
webSocketStatus,
|
||||
status,
|
||||
isLoadingMessages: messageRateHandler.isUnderThreshold,
|
||||
events,
|
||||
parsedEvents,
|
||||
send,
|
||||
}),
|
||||
[
|
||||
webSocketStatus,
|
||||
messageRateHandler.isUnderThreshold,
|
||||
events,
|
||||
parsedEvents,
|
||||
],
|
||||
[status, messageRateHandler.isUnderThreshold, events, parsedEvents],
|
||||
);
|
||||
|
||||
return <WsClientContext value={value}>{children}</WsClientContext>;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import React from "react";
|
||||
import {
|
||||
useWsClient,
|
||||
WsClientProviderStatus,
|
||||
} from "#/context/ws-client-provider";
|
||||
import { useConversationId } from "#/hooks/use-conversation-id";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { useRuntimeIsReady } from "../use-runtime-is-ready";
|
||||
|
||||
export const useConversationConfig = () => {
|
||||
const { status } = useWsClient();
|
||||
const { conversationId } = useConversationId();
|
||||
const runtimeIsReady = useRuntimeIsReady();
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ["conversation_config", conversationId],
|
||||
@@ -14,7 +17,7 @@ export const useConversationConfig = () => {
|
||||
if (!conversationId) throw new Error("No conversation ID");
|
||||
return OpenHands.getRuntimeId(conversationId);
|
||||
},
|
||||
enabled: runtimeIsReady && !!conversationId,
|
||||
enabled: status !== WsClientProviderStatus.DISCONNECTED && !!conversationId,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
interface UseConversationMicroagentsOptions {
|
||||
conversationId: string | undefined;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export const useConversationMicroagents = ({
|
||||
conversationId,
|
||||
enabled = true,
|
||||
}: UseConversationMicroagentsOptions) =>
|
||||
useQuery({
|
||||
queryKey: ["conversation", conversationId, "microagents"],
|
||||
queryFn: async () => {
|
||||
if (!conversationId) {
|
||||
throw new Error("No conversation ID provided");
|
||||
}
|
||||
const data = await OpenHands.getMicroagents(conversationId);
|
||||
return data.microagents;
|
||||
},
|
||||
enabled: !!conversationId && enabled,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useConversationId } from "../use-conversation-id";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
export const useGetMicroagentPrompt = ({ eventId }: { eventId: number }) => {
|
||||
const { conversationId } = useConversationId();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["conversation", "remember_prompt", conversationId, eventId],
|
||||
queryFn: () => OpenHands.getMicroagentPrompt(conversationId, eventId),
|
||||
});
|
||||
};
|
||||
@@ -248,9 +248,6 @@ export enum I18nKey {
|
||||
INVARIANT$TRACE_EXPORTED_MESSAGE = "INVARIANT$TRACE_EXPORTED_MESSAGE",
|
||||
INVARIANT$POLICY_UPDATED_MESSAGE = "INVARIANT$POLICY_UPDATED_MESSAGE",
|
||||
INVARIANT$SETTINGS_UPDATED_MESSAGE = "INVARIANT$SETTINGS_UPDATED_MESSAGE",
|
||||
CHAT_INTERFACE$DISCONNECTED = "CHAT_INTERFACE$DISCONNECTED",
|
||||
CHAT_INTERFACE$CONNECTING = "CHAT_INTERFACE$CONNECTING",
|
||||
CHAT_INTERFACE$STOPPED = "CHAT_INTERFACE$STOPPED",
|
||||
CHAT_INTERFACE$INITIALIZING_AGENT_LOADING_MESSAGE = "CHAT_INTERFACE$INITIALIZING_AGENT_LOADING_MESSAGE",
|
||||
CHAT_INTERFACE$AGENT_INIT_MESSAGE = "CHAT_INTERFACE$AGENT_INIT_MESSAGE",
|
||||
CHAT_INTERFACE$AGENT_RUNNING_MESSAGE = "CHAT_INTERFACE$AGENT_RUNNING_MESSAGE",
|
||||
@@ -369,8 +366,10 @@ export enum I18nKey {
|
||||
FEEDBACK$PUBLIC_LABEL = "FEEDBACK$PUBLIC_LABEL",
|
||||
FEEDBACK$PRIVATE_LABEL = "FEEDBACK$PRIVATE_LABEL",
|
||||
SIDEBAR$CONVERSATIONS = "SIDEBAR$CONVERSATIONS",
|
||||
STATUS$CONNECTING_TO_RUNTIME = "STATUS$CONNECTING_TO_RUNTIME",
|
||||
STATUS$STARTING_RUNTIME = "STATUS$STARTING_RUNTIME",
|
||||
STATUS$STARTING_CONTAINER = "STATUS$STARTING_CONTAINER",
|
||||
STATUS$PREPARING_CONTAINER = "STATUS$PREPARING_CONTAINER",
|
||||
STATUS$CONTAINER_STARTED = "STATUS$CONTAINER_STARTED",
|
||||
STATUS$SETTING_UP_WORKSPACE = "STATUS$SETTING_UP_WORKSPACE",
|
||||
STATUS$SETTING_UP_GIT_HOOKS = "STATUS$SETTING_UP_GIT_HOOKS",
|
||||
ACCOUNT_SETTINGS_MODAL$DISCONNECT = "ACCOUNT_SETTINGS_MODAL$DISCONNECT",
|
||||
@@ -461,7 +460,7 @@ export enum I18nKey {
|
||||
CONVERSATION$NO_CONVERSATIONS = "CONVERSATION$NO_CONVERSATIONS",
|
||||
LANDING$SELECT_GIT_REPO = "LANDING$SELECT_GIT_REPO",
|
||||
BUTTON$SEND = "BUTTON$SEND",
|
||||
STATUS$BUILDING_RUNTIME = "STATUS$BUILDING_RUNTIME",
|
||||
STATUS$WAITING_FOR_CLIENT = "STATUS$WAITING_FOR_CLIENT",
|
||||
SUGGESTIONS$WHAT_TO_BUILD = "SUGGESTIONS$WHAT_TO_BUILD",
|
||||
SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL = "SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL",
|
||||
BUTTON$MARK_HELPFUL = "BUTTON$MARK_HELPFUL",
|
||||
@@ -471,7 +470,6 @@ export enum I18nKey {
|
||||
BILLING$YOUVE_GOT_50 = "BILLING$YOUVE_GOT_50",
|
||||
BILLING$ERROR_WHILE_CREATING_SESSION = "BILLING$ERROR_WHILE_CREATING_SESSION",
|
||||
BILLING$CLAIM_YOUR_50 = "BILLING$CLAIM_YOUR_50",
|
||||
BILLING$POWERED_BY = "BILLING$POWERED_BY",
|
||||
BILLING$PROCEED_TO_STRIPE = "BILLING$PROCEED_TO_STRIPE",
|
||||
BILLING$YOURE_IN = "BILLING$YOURE_IN",
|
||||
PAYMENT$ADD_FUNDS = "PAYMENT$ADD_FUNDS",
|
||||
@@ -545,16 +543,6 @@ export enum I18nKey {
|
||||
TOS$CONTINUE = "TOS$CONTINUE",
|
||||
TOS$ERROR_ACCEPTING = "TOS$ERROR_ACCEPTING",
|
||||
TIPS$CUSTOMIZE_MICROAGENT = "TIPS$CUSTOMIZE_MICROAGENT",
|
||||
CONVERSATION$SHOW_MICROAGENTS = "CONVERSATION$SHOW_MICROAGENTS",
|
||||
CONVERSATION$NO_MICROAGENTS = "CONVERSATION$NO_MICROAGENTS",
|
||||
CONVERSATION$FAILED_TO_FETCH_MICROAGENTS = "CONVERSATION$FAILED_TO_FETCH_MICROAGENTS",
|
||||
MICROAGENTS_MODAL$TITLE = "MICROAGENTS_MODAL$TITLE",
|
||||
MICROAGENTS_MODAL$TRIGGERS = "MICROAGENTS_MODAL$TRIGGERS",
|
||||
MICROAGENTS_MODAL$INPUTS = "MICROAGENTS_MODAL$INPUTS",
|
||||
MICROAGENTS_MODAL$TOOLS = "MICROAGENTS_MODAL$TOOLS",
|
||||
MICROAGENTS_MODAL$CONTENT = "MICROAGENTS_MODAL$CONTENT",
|
||||
MICROAGENTS_MODAL$NO_CONTENT = "MICROAGENTS_MODAL$NO_CONTENT",
|
||||
MICROAGENTS_MODAL$FETCH_ERROR = "MICROAGENTS_MODAL$FETCH_ERROR",
|
||||
TIPS$SETUP_SCRIPT = "TIPS$SETUP_SCRIPT",
|
||||
TIPS$VSCODE_INSTANCE = "TIPS$VSCODE_INSTANCE",
|
||||
TIPS$SAVE_WORK = "TIPS$SAVE_WORK",
|
||||
|
||||
@@ -3967,69 +3967,21 @@
|
||||
"ja": "設定を更新しました",
|
||||
"uk": "Налаштування оновлено"
|
||||
},
|
||||
"CHAT_INTERFACE$DISCONNECTED": {
|
||||
"en": "Disconnected",
|
||||
"ja": "切断されました",
|
||||
"zh-CN": "已断开连接",
|
||||
"zh-TW": "已斷開連接",
|
||||
"ko-KR": "연결 끊김",
|
||||
"no": "Frakoblet",
|
||||
"it": "Disconnesso",
|
||||
"pt": "Desconectado",
|
||||
"es": "Desconectado",
|
||||
"ar": "تم قطع الاتصال",
|
||||
"fr": "Déconnecté",
|
||||
"tr": "Bağlantı kesildi",
|
||||
"de": "Getrennt",
|
||||
"uk": "Від'єднано"
|
||||
},
|
||||
"CHAT_INTERFACE$CONNECTING": {
|
||||
"en": "Connecting",
|
||||
"ja": "接続中",
|
||||
"zh-CN": "正在连接",
|
||||
"zh-TW": "正在連接",
|
||||
"ko-KR": "연결 중",
|
||||
"no": "Kobler til",
|
||||
"it": "Connessione in corso",
|
||||
"pt": "Conectando",
|
||||
"es": "Conectando",
|
||||
"ar": "جاري الاتصال",
|
||||
"fr": "Connexion en cours",
|
||||
"tr": "Bağlanıyor",
|
||||
"de": "Verbindung wird hergestellt",
|
||||
"uk": "З'єднання"
|
||||
},
|
||||
"CHAT_INTERFACE$STOPPED": {
|
||||
"en": "Stopped",
|
||||
"ja": "停止しました",
|
||||
"zh-CN": "已停止",
|
||||
"zh-TW": "已停止",
|
||||
"ko-KR": "중지됨",
|
||||
"no": "Stoppet",
|
||||
"it": "Fermato",
|
||||
"pt": "Parado",
|
||||
"es": "Detenido",
|
||||
"ar": "توقف",
|
||||
"fr": "Arrêté",
|
||||
"tr": "Durduruldu",
|
||||
"de": "Angehalten",
|
||||
"uk": "Зупинено"
|
||||
},
|
||||
"CHAT_INTERFACE$INITIALIZING_AGENT_LOADING_MESSAGE": {
|
||||
"en": "Initializing Agent...",
|
||||
"de": "Agent wird initialisiert...",
|
||||
"zh-CN": "正在初始化智能体...",
|
||||
"zh-TW": "正在初始化智能體...",
|
||||
"ko-KR": "에이전트 초기화 중...",
|
||||
"no": "Initialiserer agent...",
|
||||
"it": "Inizializzazione dell'agente...",
|
||||
"pt": "Inicializando o Agente...",
|
||||
"es": "Inicializando Agente...",
|
||||
"ar": "جارٍ تهيئة الوكيل...",
|
||||
"fr": "Initialisation de l'agent...",
|
||||
"tr": "Ajan başlatılıyor...",
|
||||
"ja": "エージェントを初期化中...",
|
||||
"uk": "Ініціалізація агента..."
|
||||
"en": "Starting up!",
|
||||
"de": "Wird gestartet!",
|
||||
"zh-CN": "正在启动!",
|
||||
"zh-TW": "正在啟動!",
|
||||
"ko-KR": "시작 중입니다!",
|
||||
"no": "Starter opp!",
|
||||
"it": "Avvio in corso!",
|
||||
"pt": "Iniciando!",
|
||||
"es": "¡Iniciando!",
|
||||
"ar": "جارٍ البدء!",
|
||||
"fr": "Démarrage en cours !",
|
||||
"tr": "Başlatılıyor!",
|
||||
"ja": "起動中!",
|
||||
"uk": "Запуск!"
|
||||
},
|
||||
"CHAT_INTERFACE$AGENT_INIT_MESSAGE": {
|
||||
"en": "Agent is initialized, waiting for task...",
|
||||
@@ -5903,22 +5855,6 @@
|
||||
"tr": "Konuşmalar",
|
||||
"uk": "Розмови"
|
||||
},
|
||||
"STATUS$CONNECTING_TO_RUNTIME": {
|
||||
"en": "Connecting to runtime...",
|
||||
"zh-CN": "正在连接到运行时...",
|
||||
"zh-TW": "正在連接到執行時...",
|
||||
"de": "Verbinde mit der Laufzeitumgebung...",
|
||||
"ko-KR": "런타임에 연결 중...",
|
||||
"no": "Kobler til kjøretidsmiljø...",
|
||||
"it": "Connessione all'ambiente di esecuzione in corso...",
|
||||
"pt": "Conectando ao ambiente de execução...",
|
||||
"es": "Conectando al entorno de ejecución...",
|
||||
"ar": "جارٍ الاتصال ببيئة التشغيل...",
|
||||
"fr": "Connexion à l'environnement d'exécution en cours...",
|
||||
"tr": "Çalışma zamanı ortamına bağlanılıyor...",
|
||||
"ja": "ランタイムに接続中",
|
||||
"uk": "Підключення до середовища виконання..."
|
||||
},
|
||||
"STATUS$STARTING_RUNTIME": {
|
||||
"en": "Starting runtime...",
|
||||
"zh-CN": "启动运行时...",
|
||||
@@ -5935,6 +5871,54 @@
|
||||
"ja": "ランタイムを開始中",
|
||||
"uk": "Запуск середовища виконання..."
|
||||
},
|
||||
"STATUS$STARTING_CONTAINER": {
|
||||
"en": "Preparing container, this might take a few minutes...",
|
||||
"zh-CN": "正在准备容器,这可能需要几分钟...",
|
||||
"zh-TW": "正在準備容器,這可能需要幾分鐘...",
|
||||
"de": "Container wird vorbereitet, dies kann einige Minuten dauern...",
|
||||
"ko-KR": "컨테이너를 준비 중입니다. 몇 분 정도 걸릴 수 있습니다...",
|
||||
"no": "Forbereder container, dette kan ta noen minutter...",
|
||||
"it": "Preparazione del container in corso, potrebbe richiedere alcuni minuti...",
|
||||
"pt": "Preparando o container, isso pode levar alguns minutos...",
|
||||
"es": "Preparando el contenedor, esto puede tardar unos minutos...",
|
||||
"ar": "جارٍ إعداد الحاوية، قد يستغرق هذا بضع دقائق...",
|
||||
"fr": "Préparation du conteneur, cela peut prendre quelques minutes...",
|
||||
"tr": "Konteyner hazırlanıyor, bu işlem birkaç dakika sürebilir...",
|
||||
"ja": "コンテナを開始中",
|
||||
"uk": "Підготовка контейнера, це може тривати кілька хвилин..."
|
||||
},
|
||||
"STATUS$PREPARING_CONTAINER": {
|
||||
"en": "Preparing to start container...",
|
||||
"zh-CN": "正在准备启动容器...",
|
||||
"zh-TW": "正在準備啟動容器...",
|
||||
"de": "Vorbereitung zum Starten des Containers...",
|
||||
"ko-KR": "컨테이너 시작 준비 중...",
|
||||
"no": "Forbereder å starte container...",
|
||||
"it": "Preparazione all'avvio del container...",
|
||||
"pt": "Preparando para iniciar o container...",
|
||||
"es": "Preparando para iniciar el contenedor...",
|
||||
"ar": "جارٍ التحضير لبدء الحاوية...",
|
||||
"fr": "Préparation du démarrage du conteneur...",
|
||||
"tr": "Konteyner başlatılmaya hazırlanıyor...",
|
||||
"ja": "コンテナを準備中",
|
||||
"uk": "Preparing to start container..."
|
||||
},
|
||||
"STATUS$CONTAINER_STARTED": {
|
||||
"en": "Container started.",
|
||||
"zh-CN": "容器已启动。",
|
||||
"zh-TW": "容器已啟動。",
|
||||
"de": "Container gestartet.",
|
||||
"ko-KR": "컨테이너가 시작되었습니다.",
|
||||
"no": "Container startet.",
|
||||
"it": "Container avviato.",
|
||||
"pt": "Container iniciado.",
|
||||
"es": "Contenedor iniciado.",
|
||||
"ar": "تم بدء الحاوية.",
|
||||
"fr": "Conteneur démarré.",
|
||||
"tr": "Konteyner başlatıldı.",
|
||||
"ja": "コンテナが開始されました",
|
||||
"uk": "Контейнер запущено."
|
||||
},
|
||||
"STATUS$SETTING_UP_WORKSPACE": {
|
||||
"en": "Setting up workspace...",
|
||||
"zh-CN": "正在设置工作区...",
|
||||
@@ -7375,21 +7359,21 @@
|
||||
"tr": "Gönder",
|
||||
"uk": "Надіслати"
|
||||
},
|
||||
"STATUS$BUILDING_RUNTIME": {
|
||||
"en": "Building Runtime...",
|
||||
"ja": "ランタイムを構築中...",
|
||||
"zh-CN": "正在构建运行时...",
|
||||
"zh-TW": "正在構建運行時...",
|
||||
"ko-KR": "런타임 구축 중...",
|
||||
"no": "Bygger kjøretidsmiljø...",
|
||||
"it": "Costruzione del runtime in corso...",
|
||||
"pt": "Construindo ambiente de execução...",
|
||||
"es": "Construyendo entorno de ejecución...",
|
||||
"ar": "جاري بناء بيئة التشغيل...",
|
||||
"fr": "Construction de l'environnement d'exécution...",
|
||||
"tr": "Çalışma ortamı oluşturuluyor...",
|
||||
"de": "Laufzeitumgebung wird erstellt...",
|
||||
"uk": "Створення середовища виконання..."
|
||||
"STATUS$WAITING_FOR_CLIENT": {
|
||||
"en": "Waiting for client to become ready...",
|
||||
"zh-CN": "等待客户端准备就绪...",
|
||||
"zh-TW": "等待客戶端準備就緒...",
|
||||
"de": "Warten auf Bereitschaft des Clients...",
|
||||
"ko-KR": "클라이언트가 준비될 때까지 기다리는 중...",
|
||||
"no": "Venter på at klienten skal bli klar...",
|
||||
"it": "In attesa che il client sia pronto...",
|
||||
"pt": "Aguardando o cliente ficar pronto...",
|
||||
"es": "Esperando a que el cliente esté listo...",
|
||||
"ar": "في انتظار جاهزية العميل...",
|
||||
"fr": "En attente que le client soit prêt...",
|
||||
"tr": "İstemcinin hazır olması bekleniyor...",
|
||||
"ja": "クライアントの準備を待機中",
|
||||
"uk": "Чекаємо на готовність клієнта..."
|
||||
},
|
||||
"SUGGESTIONS$WHAT_TO_BUILD": {
|
||||
"en": "What do you want to build?",
|
||||
@@ -7535,22 +7519,6 @@
|
||||
"de": "Fügen Sie eine Kreditkarte mit Stripe hinzu, um $50 zu erhalten. <b>Wir belasten Sie nicht ohne vorherige Zustimmung!</b>",
|
||||
"uk": "Додайте кредитну картку до Stripe, щоб отримати свої 50 доларів. <b>Ми не стягуватимемо з вас плату без попереднього запиту!</b>"
|
||||
},
|
||||
"BILLING$POWERED_BY": {
|
||||
"en": "Powered by",
|
||||
"ja": "提供:",
|
||||
"zh-CN": "技术支持:",
|
||||
"zh-TW": "技術支援:",
|
||||
"ko-KR": "제공: ",
|
||||
"no": "Drevet av",
|
||||
"it": "Offerto da",
|
||||
"pt": "Oferecido por",
|
||||
"es": "Ofrecido por",
|
||||
"ar": "مشغل بواسطة",
|
||||
"fr": "Propulsé par",
|
||||
"tr": "Tarafından desteklenmektedir",
|
||||
"de": "Bereitgestellt von",
|
||||
"uk": "Працює на базі"
|
||||
},
|
||||
"BILLING$PROCEED_TO_STRIPE": {
|
||||
"en": "Add Billing Info",
|
||||
"ja": "請求情報を追加",
|
||||
@@ -8704,180 +8672,20 @@
|
||||
"tr": "Hizmet Şartlarını kabul ederken hata oluştu"
|
||||
},
|
||||
"TIPS$CUSTOMIZE_MICROAGENT": {
|
||||
"en": "You can customize OpenHands for your repo using an available microagent. Ask OpenHands to put a description of the repo, including how to run the code, into .openhands/microagents/repo.md.",
|
||||
"ja": "利用可能なマイクロエージェントを使用して、リポジトリ用にOpenHandsをカスタマイズできます。OpenHandsに、コードの実行方法を含むリポジトリの説明を.openhands/microagents/repo.mdに入れるよう依頼してください。",
|
||||
"zh-CN": "您可以使用可用微代理为您的仓库自定义OpenHands。请OpenHands将仓库的描述(包括如何运行代码)放入.openhands/microagents/repo.md。",
|
||||
"zh-TW": "您可以使用可用微代理為您的倉庫自定義OpenHands。請OpenHands將倉庫的描述(包括如何運行代碼)放入.openhands/microagents/repo.md。",
|
||||
"ko-KR": "사용 가능한 마이크로에이전트를 사용하여 저장소에 맞게 OpenHands를 사용자 정의할 수 있습니다. OpenHands에게 코드 실행 방법을 포함한 저장소 설명을 .openhands/microagents/repo.md에 넣도록 요청하세요.",
|
||||
"no": "Du kan tilpasse OpenHands for ditt repo ved å bruke en tilgjengelig mikroagent. Be OpenHands om å legge en beskrivelse av repoet, inkludert hvordan du kjører koden, i .openhands/microagents/repo.md.",
|
||||
"it": "Puoi personalizzare OpenHands per il tuo repository utilizzando un microagente disponibile. Chiedi a OpenHands di inserire una descrizione del repository, incluso come eseguire il codice, in .openhands/microagents/repo.md.",
|
||||
"pt": "Você pode personalizar o OpenHands para seu repositório usando um microagente disponível. Peça ao OpenHands para colocar uma descrição do repositório, incluindo como executar o código, em .openhands/microagents/repo.md.",
|
||||
"es": "Puede personalizar OpenHands para su repositorio utilizando un microagente disponible. Pídale a OpenHands que ponga una descripción del repositorio, incluido cómo ejecutar el código, en .openhands/microagents/repo.md.",
|
||||
"ar": "يمكنك تخصيص OpenHands لمستودعك باستخدام وكيل مصغر متاح. اطلب من OpenHands وضع وصف للمستودع، بما في ذلك كيفية تشغيل الكود، في .openhands/microagents/repo.md.",
|
||||
"de": "Sie können OpenHands für Ihr Repository mit einem verfügbaren Mikroagenten anpassen. Bitten Sie OpenHands, eine Beschreibung des Repositorys, einschließlich der Ausführung des Codes, in .openhands/microagents/repo.md zu platzieren.",
|
||||
"fr": "Vous pouvez personnaliser OpenHands pour votre dépôt en utilisant un micro-agent disponible. Demandez à OpenHands de mettre une description du dépôt, y compris comment exécuter le code, dans .openhands/microagents/repo.md.",
|
||||
"tr": "Kullanılabilir bir mikro ajan kullanarak OpenHands'i deponuz için özelleştirebilirsiniz. OpenHands'ten deponun açıklamasını, kodun nasıl çalıştırılacağı dahil, .openhands/microagents/repo.md dosyasına koymasını isteyin.",
|
||||
"uk": "Ви можете налаштувати OpenHands для свого репозиторію за допомогою доступного мікроагента. Попросіть OpenHands розмістити опис репозиторію, включаючи інформацію про те, як запустити код, у файлі .openhands/microagents/repo.md."
|
||||
},
|
||||
"CONVERSATION$SHOW_MICROAGENTS": {
|
||||
"en": "Show Available Microagents",
|
||||
"ja": "利用可能なマイクロエージェントを表示",
|
||||
"zh-CN": "显示可用微代理",
|
||||
"zh-TW": "顯示可用微代理",
|
||||
"ko-KR": "사용 가능한 마이크로에이전트 표시",
|
||||
"no": "Vis tilgjengelige mikroagenter",
|
||||
"ar": "عرض الوكلاء المصغرين المتاحة",
|
||||
"de": "Verfügbare Mikroagenten anzeigen",
|
||||
"fr": "Afficher les micro-agents disponibles",
|
||||
"it": "Mostra microagenti disponibili",
|
||||
"pt": "Mostrar microagentes disponíveis",
|
||||
"es": "Mostrar microagentes disponibles",
|
||||
"tr": "Kullanılabilir mikro ajanları göster",
|
||||
"uk": "Показати доступних мікроагентів"
|
||||
},
|
||||
"CONVERSATION$NO_MICROAGENTS": {
|
||||
"en": "No available microagents found for this conversation.",
|
||||
"ja": "この会話用の利用可能なマイクロエージェントが見つかりませんでした。",
|
||||
"zh-CN": "未找到此对话的可用微代理。",
|
||||
"zh-TW": "未找到此對話的可用微代理。",
|
||||
"ko-KR": "이 대화에 대한 사용 가능한 마이크로에이전트를 찾을 수 없습니다.",
|
||||
"no": "Ingen tilgjengelige mikroagenter funnet for denne samtalen.",
|
||||
"ar": "لم يتم العثور على وكلاء مصغرين متاحة لهذه المحادثة.",
|
||||
"de": "Keine verfügbaren Mikroagenten für dieses Gespräch gefunden.",
|
||||
"fr": "Aucun micro-agent disponible trouvé pour cette conversation.",
|
||||
"it": "Nessun microagente disponibile trovato per questa conversazione.",
|
||||
"pt": "Nenhum microagente disponível encontrado para esta conversa.",
|
||||
"es": "No se encontraron microagentes disponibles para esta conversación.",
|
||||
"tr": "Bu konuşma için kullanılabilir mikro ajan bulunamadı.",
|
||||
"uk": "Для цієї розмови не знайдено доступних мікроагентів."
|
||||
},
|
||||
"CONVERSATION$FAILED_TO_FETCH_MICROAGENTS": {
|
||||
"en": "Failed to fetch available microagents",
|
||||
"ja": "利用可能なマイクロエージェントの取得に失敗しました",
|
||||
"zh-CN": "获取可用微代理失败",
|
||||
"zh-TW": "獲取可用微代理失敗",
|
||||
"ko-KR": "사용 가능한 마이크로에이전트를 가져오지 못했습니다",
|
||||
"no": "Kunne ikke hente tilgjengelige mikroagenter",
|
||||
"ar": "فشل في جلب الوكلاء المصغرين المتاحة",
|
||||
"de": "Fehler beim Abrufen von verfügbaren Mikroagenten",
|
||||
"fr": "Échec de la récupération des micro-agents disponibles",
|
||||
"it": "Impossibile recuperare i microagenti disponibili",
|
||||
"pt": "Falha ao buscar microagentes disponíveis",
|
||||
"es": "Error al obtener microagentes disponibles",
|
||||
"tr": "Kullanılabilir mikro ajanlar getirilemedi",
|
||||
"uk": "Не вдалося отримати доступних мікроагентів"
|
||||
},
|
||||
"MICROAGENTS_MODAL$TITLE": {
|
||||
"en": "Available Microagents",
|
||||
"ja": "利用可能なマイクロエージェント",
|
||||
"zh-CN": "可用微代理",
|
||||
"zh-TW": "可用微代理",
|
||||
"ko-KR": "사용 가능한 마이크로에이전트",
|
||||
"no": "Tilgjengelige mikroagenter",
|
||||
"ar": "الوكلاء المصغرين المتاحة",
|
||||
"de": "Verfügbare Mikroagenten",
|
||||
"fr": "Micro-agents disponibles",
|
||||
"it": "Microagenti disponibili",
|
||||
"pt": "Microagentes disponíveis",
|
||||
"es": "Microagentes disponibles",
|
||||
"tr": "Kullanılabilir mikro ajanlar",
|
||||
"uk": "Доступні мікроагенти"
|
||||
},
|
||||
"MICROAGENTS_MODAL$TRIGGERS": {
|
||||
"en": "Triggers",
|
||||
"ja": "トリガー",
|
||||
"zh-CN": "触发器",
|
||||
"zh-TW": "觸發器",
|
||||
"ko-KR": "트리거",
|
||||
"no": "Utløsere",
|
||||
"ar": "المحفزات",
|
||||
"de": "Auslöser",
|
||||
"fr": "Déclencheurs",
|
||||
"it": "Trigger",
|
||||
"pt": "Gatilhos",
|
||||
"es": "Disparadores",
|
||||
"tr": "Tetikleyiciler",
|
||||
"uk": "Тригери"
|
||||
},
|
||||
"MICROAGENTS_MODAL$INPUTS": {
|
||||
"en": "Inputs",
|
||||
"ja": "入力",
|
||||
"zh-CN": "输入",
|
||||
"zh-TW": "輸入",
|
||||
"ko-KR": "입력",
|
||||
"no": "Inndata",
|
||||
"ar": "المدخلات",
|
||||
"de": "Eingaben",
|
||||
"fr": "Entrées",
|
||||
"it": "Input",
|
||||
"pt": "Entradas",
|
||||
"es": "Entradas",
|
||||
"tr": "Girdiler",
|
||||
"uk": "Вхідні дані"
|
||||
},
|
||||
"MICROAGENTS_MODAL$TOOLS": {
|
||||
"en": "Tools",
|
||||
"ja": "ツール",
|
||||
"zh-CN": "工具",
|
||||
"zh-TW": "工具",
|
||||
"ko-KR": "도구",
|
||||
"no": "Verktøy",
|
||||
"ar": "الأدوات",
|
||||
"de": "Werkzeuge",
|
||||
"fr": "Outils",
|
||||
"it": "Strumenti",
|
||||
"pt": "Ferramentas",
|
||||
"es": "Herramientas",
|
||||
"tr": "Araçlar",
|
||||
"uk": "Інструменти"
|
||||
},
|
||||
"MICROAGENTS_MODAL$CONTENT": {
|
||||
"en": "Content",
|
||||
"ja": "コンテンツ",
|
||||
"zh-CN": "内容",
|
||||
"zh-TW": "內容",
|
||||
"ko-KR": "콘텐츠",
|
||||
"no": "Innhold",
|
||||
"ar": "المحتوى",
|
||||
"de": "Inhalt",
|
||||
"fr": "Contenu",
|
||||
"it": "Contenuto",
|
||||
"pt": "Conteúdo",
|
||||
"es": "Contenido",
|
||||
"tr": "İçerik",
|
||||
"uk": "Вміст"
|
||||
},
|
||||
"MICROAGENTS_MODAL$NO_CONTENT": {
|
||||
"en": "Microagent has no content",
|
||||
"ja": "マイクロエージェントにコンテンツがありません",
|
||||
"zh-CN": "微代理没有内容",
|
||||
"zh-TW": "微代理沒有內容",
|
||||
"ko-KR": "마이크로에이전트에 콘텐츠가 없습니다",
|
||||
"no": "Mikroagenten har ikke innhold",
|
||||
"ar": "الوكيل المصغر ليس لديه محتوى",
|
||||
"de": "Mikroagent hat keinen Inhalt",
|
||||
"fr": "Le micro-agent n'a pas de contenu",
|
||||
"it": "Il microagente non ha contenuto",
|
||||
"pt": "Microagente não tem conteúdo",
|
||||
"es": "El microagente no tiene contenido",
|
||||
"tr": "Mikroajanın içeriği yok",
|
||||
"uk": "Мікроагент не має вмісту"
|
||||
},
|
||||
"MICROAGENTS_MODAL$FETCH_ERROR": {
|
||||
"en": "Failed to fetch microagents. Please try again later.",
|
||||
"ja": "マイクロエージェントの取得に失敗しました。後でもう一度お試しください。",
|
||||
"zh-CN": "获取微代理失败。请稍后再试。",
|
||||
"zh-TW": "獲取微代理失敗。請稍後再試。",
|
||||
"ko-KR": "마이크로에이전트를 가져오지 못했습니다. 나중에 다시 시도해 주세요.",
|
||||
"no": "Kunne ikke hente mikroagenter. Prøv igjen senere.",
|
||||
"ar": "فشل في جلب الوكلاء المصغرين. يرجى المحاولة مرة أخرى لاحقًا.",
|
||||
"de": "Mikroagenten konnten nicht abgerufen werden. Bitte versuchen Sie es später erneut.",
|
||||
"fr": "Échec de la récupération des micro-agents. Veuillez réessayer plus tard.",
|
||||
"it": "Impossibile recuperare i microagenti. Riprova più tardi.",
|
||||
"pt": "Falha ao buscar microagentes. Por favor, tente novamente mais tarde.",
|
||||
"es": "Error al obtener microagentes. Por favor, inténtelo de nuevo más tarde.",
|
||||
"tr": "Mikroajanlar getirilemedi. Lütfen daha sonra tekrar deneyin.",
|
||||
"uk": "Не вдалося отримати мікроагентів. Будь ласка, спробуйте пізніше."
|
||||
"en": "You can customize OpenHands for your repo using a microagent. Ask OpenHands to put a description of the repo, including how to run the code, into .openhands/microagents/repo.md.",
|
||||
"ja": "マイクロエージェントを使用して、リポジトリ用にOpenHandsをカスタマイズできます。OpenHandsに、コードの実行方法を含むリポジトリの説明を.openhands/microagents/repo.mdに入れるよう依頼してください。",
|
||||
"zh-CN": "您可以使用微代理为您的仓库自定义OpenHands。请OpenHands将仓库的描述(包括如何运行代码)放入.openhands/microagents/repo.md。",
|
||||
"zh-TW": "您可以使用微代理為您的倉庫自定義OpenHands。請OpenHands將倉庫的描述(包括如何運行代碼)放入.openhands/microagents/repo.md。",
|
||||
"ko-KR": "마이크로에이전트를 사용하여 저장소에 맞게 OpenHands를 사용자 정의할 수 있습니다. OpenHands에게 코드 실행 방법을 포함한 저장소 설명을 .openhands/microagents/repo.md에 넣도록 요청하세요.",
|
||||
"no": "Du kan tilpasse OpenHands for ditt repo ved å bruke en mikroagent. Be OpenHands om å legge en beskrivelse av repoet, inkludert hvordan du kjører koden, i .openhands/microagents/repo.md.",
|
||||
"it": "Puoi personalizzare OpenHands per il tuo repository utilizzando un microagente. Chiedi a OpenHands di inserire una descrizione del repository, incluso come eseguire il codice, in .openhands/microagents/repo.md.",
|
||||
"pt": "Você pode personalizar o OpenHands para seu repositório usando um microagente. Peça ao OpenHands para colocar uma descrição do repositório, incluindo como executar o código, em .openhands/microagents/repo.md.",
|
||||
"es": "Puede personalizar OpenHands para su repositorio utilizando un microagente. Pídale a OpenHands que ponga una descripción del repositorio, incluido cómo ejecutar el código, en .openhands/microagents/repo.md.",
|
||||
"ar": "يمكنك تخصيص OpenHands لمستودعك باستخدام وكيل مصغر. اطلب من OpenHands وضع وصف للمستودع، بما في ذلك كيفية تشغيل الكود، في .openhands/microagents/repo.md.",
|
||||
"fr": "Vous pouvez personnaliser OpenHands pour votre dépôt en utilisant un micro-agent. Demandez à OpenHands de mettre une description du dépôt, y compris comment exécuter le code, dans .openhands/microagents/repo.md.",
|
||||
"tr": "Bir mikro ajan kullanarak deponuz için OpenHands'i özelleştirebilirsiniz. OpenHands'ten kodun nasıl çalıştırılacağı da dahil olmak üzere deponun açıklamasını .openhands/microagents/repo.md dosyasına koymasını isteyin.",
|
||||
"de": "Sie können OpenHands für Ihr Repository mit einem Mikroagenten anpassen. Bitten Sie OpenHands, eine Beschreibung des Repositorys, einschließlich der Ausführung des Codes, in .openhands/microagents/repo.md zu platzieren.",
|
||||
"uk": "Ви можете налаштувати OpenHands для вашого репозиторію за допомогою мікроагента. Попросіть OpenHands розмістити опис репозиторію, включаючи інструкції з запуску коду, у файлі .openhands/microagents/repo.md."
|
||||
},
|
||||
"TIPS$SETUP_SCRIPT": {
|
||||
"en": "You can add .openhands/setup.sh to your repository to automatically run a setup script every time you start an OpenHands conversation.",
|
||||
|
||||
@@ -57,7 +57,6 @@ const conversations: Conversation[] = [
|
||||
last_updated_at: new Date().toISOString(),
|
||||
created_at: new Date().toISOString(),
|
||||
status: "RUNNING",
|
||||
runtime_status: "STATUS$READY",
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -73,7 +72,6 @@ const conversations: Conversation[] = [
|
||||
).toISOString(),
|
||||
created_at: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: "STOPPED",
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -89,7 +87,6 @@ const conversations: Conversation[] = [
|
||||
).toISOString(),
|
||||
created_at: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: "STOPPED",
|
||||
runtime_status: null,
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
},
|
||||
@@ -285,7 +282,6 @@ export const handlers = [
|
||||
last_updated_at: new Date().toISOString(),
|
||||
created_at: new Date().toISOString(),
|
||||
status: "RUNNING",
|
||||
runtime_status: "STATUS$READY",
|
||||
url: null,
|
||||
session_api_key: null,
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@ const chat = ws.link(`ws://${window?.location.host}/socket.io`);
|
||||
|
||||
export const handlers: WebSocketHandler[] = [
|
||||
chat.addEventListener("connection", (connection) => {
|
||||
// @ts-expect-error - MSW v2 type incompatibility
|
||||
const io = toSocketIo(connection);
|
||||
// @ts-expect-error - accessing private property for testing purposes
|
||||
const { url }: { url: URL } = io.client.connection;
|
||||
|
||||
@@ -41,11 +41,11 @@ function EmailInputSection({
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={onEmailChange}
|
||||
className={`text-base text-white p-2 bg-base-tertiary rounded-sm border ${
|
||||
className={`text-base text-white p-2 bg-base-tertiary rounded border ${
|
||||
isEmailChanged && !isEmailValid
|
||||
? "border-red-500"
|
||||
: "border-tertiary"
|
||||
} flex-grow focus:outline-hidden focus:border-transparent focus:ring-0`}
|
||||
} flex-grow focus:outline-none focus:border-transparent focus:ring-0`}
|
||||
placeholder={t("SETTINGS$USER_EMAIL_LOADING")}
|
||||
data-testid="email-input"
|
||||
/>
|
||||
@@ -65,7 +65,7 @@ function EmailInputSection({
|
||||
type="button"
|
||||
onClick={onSaveEmail}
|
||||
disabled={!isEmailChanged || isSaving || !isEmailValid}
|
||||
className="px-4 py-2 rounded-sm bg-primary text-white hover:opacity-80 disabled:opacity-30 disabled:cursor-not-allowed disabled:text-[#0D0F11]"
|
||||
className="px-4 py-2 rounded bg-primary text-white hover:opacity-80 disabled:opacity-30 disabled:cursor-not-allowed disabled:text-[#0D0F11]"
|
||||
data-testid="save-email-button"
|
||||
>
|
||||
{isSaving ? t("SETTINGS$SAVING") : t("SETTINGS$SAVE")}
|
||||
@@ -76,7 +76,7 @@ function EmailInputSection({
|
||||
type="button"
|
||||
onClick={onResendVerification}
|
||||
disabled={isResendingVerification}
|
||||
className="px-4 py-2 rounded-sm bg-primary text-white hover:opacity-80 disabled:opacity-30 disabled:cursor-not-allowed disabled:text-[#0D0F11]"
|
||||
className="px-4 py-2 rounded bg-primary text-white hover:opacity-80 disabled:opacity-30 disabled:cursor-not-allowed disabled:text-[#0D0F11]"
|
||||
data-testid="resend-verification-button"
|
||||
>
|
||||
{isResendingVerification
|
||||
@@ -96,7 +96,7 @@ function VerificationAlert() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-sm mt-4"
|
||||
className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mt-4"
|
||||
role="alert"
|
||||
>
|
||||
<p className="font-bold">{t("SETTINGS$EMAIL_VERIFICATION_REQUIRED")}</p>
|
||||
@@ -205,7 +205,7 @@ function UserSettingsScreen() {
|
||||
<div data-testid="user-settings-screen" className="flex flex-col h-full">
|
||||
<div className="p-9 flex flex-col gap-6">
|
||||
{isLoading ? (
|
||||
<div className="animate-pulse h-8 w-64 bg-tertiary rounded-sm" />
|
||||
<div className="animate-pulse h-8 w-64 bg-tertiary rounded" />
|
||||
) : (
|
||||
<EmailInputSection
|
||||
email={email}
|
||||
|
||||
@@ -76,7 +76,7 @@ function VSCodeTab() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleOpenInNewTab}
|
||||
className="px-4 py-2 bg-primary text-white rounded-sm hover:bg-primary-dark transition-colors"
|
||||
className="px-4 py-2 bg-primary text-white rounded hover:bg-primary-dark transition-colors"
|
||||
>
|
||||
{t("VSCODE$OPEN_IN_NEW_TAB")}
|
||||
</button>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@plugin '../hero.ts';
|
||||
@config "../tailwind.config.js";
|
||||
@source '../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.button-base {
|
||||
@apply bg-tertiary border border-neutral-600 rounded-xs;
|
||||
@apply bg-tertiary border border-neutral-600 rounded;
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type ConversationStatus = "STARTING" | "RUNNING" | "STOPPED";
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @deprecated This type is deprecated and will be removed in a future version.
|
||||
* Use types in `frontend/src/types/core` instead.
|
||||
*/
|
||||
export interface ActionMessage {
|
||||
id: number;
|
||||
|
||||
|
||||