Merge branch 'main' of github.com:OpenDevin/OpenDevin into enyst/memories-condenser

This commit is contained in:
Engel Nyst
2024-06-27 06:49:22 +02:00
34 changed files with 523 additions and 189 deletions

View File

@@ -8,8 +8,8 @@ const config: Config = {
favicon: "img/logo.png",
// Set the production url of your site here
url: "https://OpenDevin.github.io",
baseUrl: "/OpenDevin/",
url: "https://docs.all-hands.dev",
baseUrl: "/",
// GitHub pages deployment config.
organizationName: "OpenDevin",

View File

@@ -134,9 +134,9 @@ le point de terminaison API avec lequel vous essayez de vous connecter. Cela arr
* Si vous êtes en cours d'exécution dans l'interface utilisateur, assurez-vous de définir le `model` dans le modal des paramètres
* Si vous êtes en cours d'exécution sans interface (via main.py), assurez-vous de définir `LLM_MODEL` dans votre env/config
* Assurez-vous de suivre les instructions spéciales de votre fournisseur de LLM
* [ollama](/OpenDevin/fr/modules/usage/llms/localLLMs)
* [Azure](/OpenDevin/fr/modules/usage/llms/azureLLMs)
* [Google](/OpenDevin/fr/modules/usage/llms/googleLLMs)
* [ollama](/fr/modules/usage/llms/localLLMs)
* [Azure](/fr/modules/usage/llms/azureLLMs)
* [Google](/fr/modules/usage/llms/googleLLMs)
* Assurez-vous que votre clé API est correcte
* Voyez si vous pouvez vous connecter au LLM en utilisant `curl`
* Essayez de [vous connecter via LiteLLM directement](https://github.com/BerriAI/litellm) pour tester votre configuration

View File

@@ -129,9 +129,9 @@ openai.NotFoundError: Error code: 404 - {'error': {'code': '404', 'message': 'Re
* 如果您在 UI 中运行,请确保在设置模式中设置 `model`
* 如果您通过 main.py 运行,请确保在环境变量/配置中设置 `LLM_MODEL`
* 确保遵循了您的 LLM 提供商的任何特殊说明
* [Ollama](/OpenDevin/zh-Hans/modules/usage/llms/localLLMs)
* [Azure](/OpenDevin/zh-Hans/modules/usage/llms/azureLLMs)
* [Google](/OpenDevin/zh-Hans/modules/usage/llms/googleLLMs)
* [Ollama](/zh-Hans/modules/usage/llms/localLLMs)
* [Azure](/zh-Hans/modules/usage/llms/azureLLMs)
* [Google](/zh-Hans/modules/usage/llms/googleLLMs)
* 确保您的 API 密钥正确无误
* 尝试使用 `curl` 连接到 LLM
* 尝试[直接通过 LiteLLM 连接](https://github.com/BerriAI/litellm)来测试您的设置

View File

@@ -0,0 +1,112 @@
# 💿 How to Create a Custom Docker Sandbox
The default OpenDevin sandbox comes with a [minimal ubuntu configuration](https://github.com/OpenDevin/OpenDevin/blob/main/containers/sandbox/Dockerfile). Your use case may need additional software installed by default. This guide will teach you how to accomplish this by utilizing a custom docker image.
## Setup
To get started running with your own Docker Sandbox image you need to ensure you can build OpenDevin locally via the following:
1. Clone the OpenDevin github repository to your local machine
2. In the root (OpenDevin/) directory, run ```make build```
3. Then run ```make run```
4. Finally navigate your browser to ```localhost:3001``` to ensure that your local build of OpenDevin is functional
Please refer to [Development.md](https://github.com/OpenDevin/OpenDevin/blob/main/Development.md) for more installation details.
> Note that the above steps will take some time to run and will require that your have python3.11, poetry (a python package manager), and Docker installed
## Create Your Docker Image
Next you must create your custom docker image, which should be debian/ubuntu based. For example if we want want OpenDevin to have access to the "node" binary, we would use the following Dockerfile:
```bash
# Start with latest ubuntu image
FROM ubuntu:latest
# Run needed updates
RUN apt-get update && apt-get install
# Install node
RUN apt-get install -y nodejs
```
Next build your docker image with the name of your choice, for example "custom_image". To do this you can create a directory and put your file inside it with the name "Dockerfile", and inside the directory run the following command:
```docker build -t custom_image .```
This will produce a new image called ```custom_image``` that will be available in Docker Engine.
> Note that in the configuration described in this document, OpenDevin will run as user "opendevin" inside the sandbox and thus all packages installed via the docker file should be available to all users on the system, not just root
>
> Installing with apt-get above installs node for all users
## Specify your custom image in config.toml file
OpenDevin configuration occurs via the top level ```config.toml``` file.
Create a ```config.toml``` file in the OpenDevin directory and enter these contents:
```
[core]
workspace_base="./workspace"
persist_sandbox=false
run_as_devin=true
sandbox_container_image="custom_image"
```
> Ensure that sandbox_container_image is set to the name of your custom image from the previous step
## Run
Run OpenDevin by running ```make run``` in the top level directory.
A lot of things will happen but ultimately the OpenDevin server and frontend should be running.
Navigate to ```localhost:3001``` and check if your desired dependencies are available.
In the case of the example above, running ```node -v``` in the terminal produces ```v18.19.1```
Congratulations!
## Technical Explanation
The relevant code is defined in [ssh_box.py](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/runtime/docker/ssh_box.py) and [image_agnostic_util.py](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/runtime/docker/image_agnostic_util.py).
In particular, ssh_box.py checks the config object for ```config.sandbox_container_image``` and then attempts to retrieve the image using [get_od_sandbox_image](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/runtime/docker/image_agnostic_util.py#L72) which is defined in image_agnostic_util.py.
When first using a custom image, it will not be found and thus it will be built (on subsequent runs the built image will be found and returned).
The custom image is built using [_build_sandbox_image()](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/runtime/docker/image_agnostic_util.py#L29), which creates a docker file using your custom_image as a base and then configures the environment for OpenDevin, like this:
```
dockerfile_content = (
f'FROM {base_image}\n'
'RUN apt update && apt install -y openssh-server wget sudo\n'
'RUN mkdir -p -m0755 /var/run/sshd\n'
'RUN mkdir -p /opendevin && mkdir -p /opendevin/logs && chmod 777 /opendevin/logs\n'
'RUN wget "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"\n'
'RUN bash Miniforge3-$(uname)-$(uname -m).sh -b -p /opendevin/miniforge3\n'
'RUN bash -c ". /opendevin/miniforge3/etc/profile.d/conda.sh && conda config --set changeps1 False && conda config --append channels conda-forge"\n'
'RUN echo "export PATH=/opendevin/miniforge3/bin:$PATH" >> ~/.bashrc\n'
'RUN echo "export PATH=/opendevin/miniforge3/bin:$PATH" >> /opendevin/bash.bashrc\n'
).strip()
```
> Note: the name of the image is modified via [_get_new_image_name()](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/runtime/docker/image_agnostic_util.py#L63) and it is the modified name that is searched for on subsequent runs
## Troubleshooting / Errors
### Error: ```useradd: UID 1000 is not unique```
If you see this error in the console output it is because OpenDevin is trying to create the opendevin user in the sandbox with a UID of 1000, however this UID is already being used in the image (for some reason). To fix this change the sandbox_user_id field in the config.toml file to a different value:
```
[core]
workspace_base="./workspace"
persist_sandbox=false
run_as_devin=true
sandbox_container_image="custom_image"
sandbox_user_id="1001"
```
### Port use errors
If you see an error about a port being in use or unavailable, try deleting all running Docker Containers (run `docker ps` and `docker rm` relevant containers) and then re-running ```make run```
## Discuss
For other issues or questions join the [Slack](https://join.slack.com/t/opendevin/shared_invite/zt-2jsrl32uf-fTeeFjNyNYxqSZt5NPY3fA) or [Discord](https://discord.gg/ESHStjSjD4) and ask!

View File

@@ -135,9 +135,9 @@ the API endpoint you're trying to connect to. Most often this happens for Azure
* If you're running inside the UI, be sure to set the `model` in the settings modal
* If you're running headless (via main.py) be sure to set `LLM_MODEL` in your env/config
* Make sure you've followed any special instructions for your LLM provider
* [ollama](/OpenDevin/modules/usage/llms/localLLMs)
* [Azure](/OpenDevin/modules/usage/llms/azureLLMs)
* [Google](/OpenDevin/modules/usage/llms/googleLLMs)
* [ollama](/modules/usage/llms/localLLMs)
* [Azure](/modules/usage/llms/azureLLMs)
* [Google](/modules/usage/llms/googleLLMs)
* Make sure your API key is correct
* See if you can connect to the LLM using `curl`
* Try [connecting via LiteLLM directly](https://github.com/BerriAI/litellm) to test your setup

View File

@@ -1,6 +1,6 @@
FROM ghcr.io/opendevin/eval-swe-bench:builder
# This Dockefile is used to build the Docker image for the evaluation of the SWE-Bench.
# This Dockerfile is used to build the Docker image for the evaluation of the SWE-Bench.
# YOU SHOULD ENSURE ./eval_workspace CONTAINS THE EVALUATION WORKSPACE (testbed, conda)
# Check BUILD_TESTBED_AND_ENV.md for more details.

View File

@@ -17,11 +17,11 @@
"@xterm/xterm": "^5.4.0",
"clsx": "^2.1.1",
"eslint-config-airbnb-typescript": "^18.0.0",
"framer-motion": "^11.2.11",
"framer-motion": "^11.2.12",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.5.2",
"jose": "^5.4.1",
"jose": "^5.5.0",
"monaco-editor": "^0.50.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -31,7 +31,7 @@
"react-icons": "^5.2.1",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"react-router-dom": "^6.24.0",
"react-syntax-highlighter": "^15.5.0",
"tailwind-merge": "^2.3.0",
"vite": "^5.3.1",
@@ -42,13 +42,13 @@
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^20.14.8",
"@types/node": "^20.14.9",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-highlight": "^0.12.8",
"@types/react-syntax-highlighter": "^15.5.13",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
@@ -4502,10 +4502,9 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
"integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
"license": "MIT",
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.0.tgz",
"integrity": "sha512-2D6XaHEVvkCn682XBnipbJjgZUU7xjLtA4dGJRBVUKpEaDYOZMENZoZjAOSb7qirxt5RupjzZxz4fK2FO+EFPw==",
"engines": {
"node": ">=14.0.0"
}
@@ -5075,9 +5074,9 @@
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
},
"node_modules/@types/node": {
"version": "20.14.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz",
"integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==",
"version": "20.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
"integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
"devOptional": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -5162,16 +5161,16 @@
"peer": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz",
"integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz",
"integrity": "sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.13.1",
"@typescript-eslint/type-utils": "7.13.1",
"@typescript-eslint/utils": "7.13.1",
"@typescript-eslint/visitor-keys": "7.13.1",
"@typescript-eslint/scope-manager": "7.14.1",
"@typescript-eslint/type-utils": "7.14.1",
"@typescript-eslint/utils": "7.14.1",
"@typescript-eslint/visitor-keys": "7.14.1",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -5195,15 +5194,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz",
"integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.14.1.tgz",
"integrity": "sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.13.1",
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/typescript-estree": "7.13.1",
"@typescript-eslint/visitor-keys": "7.13.1",
"@typescript-eslint/scope-manager": "7.14.1",
"@typescript-eslint/types": "7.14.1",
"@typescript-eslint/typescript-estree": "7.14.1",
"@typescript-eslint/visitor-keys": "7.14.1",
"debug": "^4.3.4"
},
"engines": {
@@ -5223,13 +5222,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz",
"integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz",
"integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/visitor-keys": "7.13.1"
"@typescript-eslint/types": "7.14.1",
"@typescript-eslint/visitor-keys": "7.14.1"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -5240,13 +5239,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz",
"integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.14.1.tgz",
"integrity": "sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.13.1",
"@typescript-eslint/utils": "7.13.1",
"@typescript-eslint/typescript-estree": "7.14.1",
"@typescript-eslint/utils": "7.14.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -5267,9 +5266,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz",
"integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz",
"integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==",
"dev": true,
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -5280,13 +5279,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz",
"integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz",
"integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/visitor-keys": "7.13.1",
"@typescript-eslint/types": "7.14.1",
"@typescript-eslint/visitor-keys": "7.14.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -5308,15 +5307,15 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz",
"integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz",
"integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "7.13.1",
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/typescript-estree": "7.13.1"
"@typescript-eslint/scope-manager": "7.14.1",
"@typescript-eslint/types": "7.14.1",
"@typescript-eslint/typescript-estree": "7.14.1"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -5330,12 +5329,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz",
"integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz",
"integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/types": "7.14.1",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -8216,9 +8215,9 @@
}
},
"node_modules/framer-motion": {
"version": "11.2.11",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.11.tgz",
"integrity": "sha512-n+ozoEzgJu/2h9NoQMokF+CwNqIRVyuRC4RwMPwklfrrTjbVV32k9uBIgqYAwn7Jfpt5LuDVCtT57MWz1FbaLw==",
"version": "11.2.12",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.12.tgz",
"integrity": "sha512-lCjkV4nA9rWOy2bhR4RZzkp2xpB++kFmUZ6D44V9VQaxk+JDmbDd5lq+u58DjJIIllE8AZEXp9OG/TyDN4FB/w==",
"dependencies": {
"tslib": "^2.4.0"
},
@@ -10553,9 +10552,9 @@
}
},
"node_modules/jose": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/jose/-/jose-5.4.1.tgz",
"integrity": "sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/jose/-/jose-5.5.0.tgz",
"integrity": "sha512-DUPr/1kYXbuqYpkCj9r66+B4SGCKXCLQ5ZbKCgmn4sJveJqcwNqWtAR56u4KPmpXjrmBO2uNuLdEAEiqIhFNBg==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
@@ -13188,12 +13187,11 @@
}
},
"node_modules/react-router": {
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
"integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
"license": "MIT",
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.0.tgz",
"integrity": "sha512-sQrgJ5bXk7vbcC4BxQxeNa5UmboFm35we1AFK0VvQaz9g0LzxEIuLOhHIoZ8rnu9BO21ishGeL9no1WB76W/eg==",
"dependencies": {
"@remix-run/router": "1.16.1"
"@remix-run/router": "1.17.0"
},
"engines": {
"node": ">=14.0.0"
@@ -13203,13 +13201,12 @@
}
},
"node_modules/react-router-dom": {
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
"integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
"license": "MIT",
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.0.tgz",
"integrity": "sha512-960sKuau6/yEwS8e+NVEidYQb1hNjAYM327gjEyXlc6r3Skf2vtwuJ2l7lssdegD2YjoKG5l8MsVyeTDlVeY8g==",
"dependencies": {
"@remix-run/router": "1.16.1",
"react-router": "6.23.1"
"@remix-run/router": "1.17.0",
"react-router": "6.24.0"
},
"engines": {
"node": ">=14.0.0"

View File

@@ -16,11 +16,11 @@
"@xterm/xterm": "^5.4.0",
"clsx": "^2.1.1",
"eslint-config-airbnb-typescript": "^18.0.0",
"framer-motion": "^11.2.11",
"framer-motion": "^11.2.12",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.5.2",
"jose": "^5.4.1",
"jose": "^5.5.0",
"monaco-editor": "^0.50.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -30,7 +30,7 @@
"react-icons": "^5.2.1",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"react-router-dom": "^6.24.0",
"react-syntax-highlighter": "^15.5.0",
"tailwind-merge": "^2.3.0",
"vite": "^5.3.1",
@@ -38,7 +38,7 @@
},
"scripts": {
"start": "npm run make-i18n && vite",
"build": "tsc && vite build",
"build": "npm run make-i18n && tsc && vite build",
"test": "vitest run",
"dev_wsl": "VITE_WATCH_USE_POLLING=true vite",
"preview": "vite preview",
@@ -64,13 +64,13 @@
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^20.14.8",
"@types/node": "^20.14.9",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-highlight": "^0.12.8",
"@types/react-syntax-highlighter": "^15.5.13",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",

BIN
frontend/public/beep.wav Normal file

Binary file not shown.

View File

@@ -11,6 +11,7 @@ import SettingsModal from "#/components/modals/settings/SettingsModal";
import "./App.css";
import AgentControlBar from "./components/AgentControlBar";
import AgentStatusBar from "./components/AgentStatusBar";
import VolumeIcon from "./components/VolumeIcon";
import Terminal from "./components/terminal/Terminal";
import Session from "#/services/session";
import { getToken } from "#/services/auth";
@@ -27,11 +28,17 @@ function Controls({ setSettingOpen }: Props): JSX.Element {
<AgentControlBar />
</div>
<AgentStatusBar />
<div
className="cursor-pointer hover:opacity-80 transition-all"
onClick={() => setSettingOpen(true)}
>
<CogTooth />
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginRight: "8px" }}>
<VolumeIcon />
</div>
<div
className="cursor-pointer hover:opacity-80 transition-all"
onClick={() => setSettingOpen(true)}
>
<CogTooth />
</div>
</div>
</div>
);

View File

@@ -15,6 +15,7 @@ const IgnoreTaskStateMap: { [k: string]: AgentState[] } = {
AgentState.PAUSED,
AgentState.STOPPED,
AgentState.FINISHED,
AgentState.REJECTED,
AgentState.AWAITING_USER_INPUT,
],
[AgentState.RUNNING]: [
@@ -22,6 +23,7 @@ const IgnoreTaskStateMap: { [k: string]: AgentState[] } = {
AgentState.RUNNING,
AgentState.STOPPED,
AgentState.FINISHED,
AgentState.REJECTED,
AgentState.AWAITING_USER_INPUT,
],
[AgentState.STOPPED]: [AgentState.INIT, AgentState.STOPPED],
@@ -48,10 +50,18 @@ function ActionButton({
<button
onClick={() => handleAction(action)}
disabled={isDisabled}
className={`${large ? "rounded-full bg-neutral-800 p-3" : ""} hover:opacity-80 transition-all`}
className={`
relative overflow-visible cursor-default hover:cursor-pointer group
disabled:cursor-not-allowed disabled:opacity-60
${large ? "rounded-full bg-neutral-800 p-3" : ""}
transition-all duration-300 ease-in-out
`}
type="button"
>
{children}
<span className="relative z-10 group-hover:filter group-hover:drop-shadow-[0_0_5px_rgba(255,64,0,0.4)]">
{children}
</span>
<span className="absolute -inset-[5px] border-2 border-red-400/40 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300 ease-in-out" />
</button>
</Tooltip>
);

View File

@@ -1,9 +1,10 @@
import React from "react";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { I18nKey } from "#/i18n/declaration";
import { RootState } from "#/store";
import AgentState from "#/types/AgentState";
import beep from "#/utils/beep";
enum IndicatorColor {
BLUE = "bg-blue-500",
@@ -49,6 +50,10 @@ function AgentStatusBar() {
message: t(I18nKey.CHAT_INTERFACE$AGENT_FINISHED_MESSAGE),
indicator: IndicatorColor.GREEN,
},
[AgentState.REJECTED]: {
message: t(I18nKey.CHAT_INTERFACE$AGENT_REJECTED_MESSAGE),
indicator: IndicatorColor.YELLOW,
},
[AgentState.ERROR]: {
message: t(I18nKey.CHAT_INTERFACE$AGENT_ERROR_MESSAGE),
indicator: IndicatorColor.RED,
@@ -61,6 +66,16 @@ function AgentStatusBar() {
// - Agent is thinking
// - Agent is ready
// - Agent is not available
useEffect(() => {
if (
curAgentState === AgentState.AWAITING_USER_INPUT ||
curAgentState === AgentState.ERROR ||
curAgentState === AgentState.INIT
) {
if (document.cookie.indexOf("audio") !== -1) beep();
}
}, [curAgentState]);
return (
<div className="flex items-center">
<div

View File

@@ -0,0 +1,29 @@
import React, { useState } from "react";
import { IoMdVolumeHigh, IoMdVolumeOff } from "react-icons/io";
import beep from "#/utils/beep";
function VolumeIcon(): JSX.Element {
const [isMuted, setIsMuted] = useState(true);
const toggleMute = () => {
const cookieName = "audio";
setIsMuted(!isMuted);
if (!isMuted) {
document.cookie = `${cookieName}=;`;
} else {
document.cookie = `${cookieName}=on;`;
beep();
}
};
return (
<div
className="cursor-pointer hover:opacity-80 transition-all"
onClick={toggleMute}
>
{isMuted ? <IoMdVolumeOff size={23} /> : <IoMdVolumeHigh size={23} />}
</div>
);
}
export default VolumeIcon;

View File

@@ -62,7 +62,7 @@ function ChatInput({ disabled = false, onSendMessage }: ChatInputProps) {
? "cursor-not-allowed border-neutral-400 text-neutral-400"
: "hover:bg-neutral-500 ",
)}
aria-label="Send message"
aria-label={t(I18nKey.CHAT_INTERFACE$TOOLTIP_SEND_MESSAGE)}
>
<VscArrowUp />
</button>

View File

@@ -1,3 +1,4 @@
// frontend/src/components/chat/ChatInterface.tsx
import React, { useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { IoMdChatbubbles } from "react-icons/io";
@@ -8,6 +9,7 @@ import { FaRegThumbsDown, FaRegThumbsUp } from "react-icons/fa";
import { useDisclosure } from "@nextui-org/react";
import ChatInput from "./ChatInput";
import Chat from "./Chat";
import TypingIndicator from "./TypingIndicator";
import { RootState } from "#/store";
import AgentState from "#/types/AgentState";
import { sendChatMessage } from "#/services/chatService";
@@ -127,19 +129,27 @@ function ChatInterface() {
<div className="relative">
<div className="absolute bottom-2 left-0 right-0 flex items-center justify-center">
{!hitBottom &&
ScrollButton({
onClick: scrollDomToBottom,
icon: <VscArrowDown className="inline mr-2 w-3 h-3" />,
label: t(I18nKey.CHAT_INTERFACE$TO_BOTTOM),
})}
{curAgentState === AgentState.AWAITING_USER_INPUT &&
hitBottom &&
ScrollButton({
onClick: handleSendContinueMsg,
icon: <RiArrowRightDoubleLine className="inline mr-2 w-3 h-3" />,
label: t(I18nKey.CHAT_INTERFACE$INPUT_CONTINUE_MESSAGE),
})}
{!hitBottom && (
<ScrollButton
onClick={scrollDomToBottom}
icon={<VscArrowDown className="inline mr-2 w-3 h-3" />}
label={t(I18nKey.CHAT_INTERFACE$TO_BOTTOM)}
/>
)}
{hitBottom && (
<>
{curAgentState === AgentState.AWAITING_USER_INPUT && (
<ScrollButton
onClick={handleSendContinueMsg}
icon={
<RiArrowRightDoubleLine className="inline mr-2 w-3 h-3" />
}
label={t(I18nKey.CHAT_INTERFACE$INPUT_CONTINUE_MESSAGE)}
/>
)}
{curAgentState === AgentState.RUNNING && <TypingIndicator />}
</>
)}
</div>
{feedbackShared !== messages.length && messages.length > 3 && (

View File

@@ -2,8 +2,10 @@ import React, { useState } from "react";
import Markdown from "react-markdown";
import { FaClipboard } from "react-icons/fa";
import { twMerge } from "tailwind-merge";
import { useTranslation } from "react-i18next";
import { code } from "../markdown/code";
import toast from "#/utils/toast";
import { I18nKey } from "#/i18n/declaration";
interface MessageProps {
message: Message;
@@ -18,14 +20,18 @@ function ChatMessage({ message }: MessageProps) {
message.sender === "user" ? "bg-neutral-700 self-end" : "bg-neutral-500",
);
const { t } = useTranslation();
const copyToClipboard = () => {
navigator.clipboard
.writeText(message.content)
.then(() => {
toast.info("Message copied to clipboard!");
toast.info(t(I18nKey.CHAT_INTERFACE$CHAT_MESSAGE_COPIED));
})
.catch((error) => {
toast.error("copy-error", `Failed to copy message: ${error}`);
.catch(() => {
toast.error(
"copy-error",
t(I18nKey.CHAT_INTERFACE$CHAT_MESSAGE_COPY_FAILED),
);
});
};
@@ -40,7 +46,7 @@ function ChatMessage({ message }: MessageProps) {
<button
onClick={copyToClipboard}
className="absolute top-1 right-1 p-1 bg-neutral-600 rounded hover:bg-neutral-500 transition-opacity opacity-75 hover:opacity-100"
aria-label="Copy message"
aria-label={t(I18nKey.CHAT_INTERFACE$TOOLTIP_COPY_MESSAGE)}
type="button"
>
<FaClipboard />

View File

@@ -0,0 +1,22 @@
import React from "react";
function TypingIndicator(): React.ReactElement {
return (
<div className="flex items-center space-x-1.5 bg-neutral-700 px-3 py-1.5 rounded-full">
<span
className="w-1.5 h-1.5 bg-gray-400 rounded-full animate-[bounce_0.5s_infinite] translate-y-[-2px]"
style={{ animationDelay: "0ms" }}
/>
<span
className="w-1.5 h-1.5 bg-gray-400 rounded-full animate-[bounce_0.5s_infinite] translate-y-[-2px]"
style={{ animationDelay: "75ms" }}
/>
<span
className="w-1.5 h-1.5 bg-gray-400 rounded-full animate-[bounce_0.5s_infinite] translate-y-[-2px]"
style={{ animationDelay: "150ms" }}
/>
</div>
);
}
export default TypingIndicator;

View File

@@ -7,6 +7,7 @@ import {
} from "react-icons/io";
import { useDispatch, useSelector } from "react-redux";
import { IoFileTray } from "react-icons/io5";
import { useTranslation } from "react-i18next";
import { twMerge } from "tailwind-merge";
import AgentState from "#/types/AgentState";
import { setRefreshID } from "#/state/codeSlice";
@@ -15,6 +16,7 @@ import IconButton from "../IconButton";
import ExplorerTree from "./ExplorerTree";
import toast from "#/utils/toast";
import { RootState } from "#/store";
import { I18nKey } from "#/i18n/declaration";
interface ExplorerActionsProps {
onRefresh: () => void;
@@ -92,7 +94,7 @@ function FileExplorer() {
const { curAgentState } = useSelector((state: RootState) => state.agent);
const fileInputRef = React.useRef<HTMLInputElement | null>(null);
const dispatch = useDispatch();
const { t } = useTranslation();
const selectFileInput = () => {
fileInputRef.current?.click(); // Trigger the file browser
};
@@ -113,7 +115,7 @@ function FileExplorer() {
await uploadFiles(toAdd);
await refreshWorkspace();
} catch (error) {
toast.error("ws", "Error uploading file");
toast.error("ws", t(I18nKey.EXPLORER$UPLOAD_ERROR_MESSAGE));
}
};
@@ -158,7 +160,9 @@ function FileExplorer() {
className="z-10 absolute flex flex-col justify-center items-center bg-black top-0 bottom-0 left-0 right-0 opacity-65"
>
<IoFileTray size={32} />
<p className="font-bold text-xl">Drop Files Here</p>
<p className="font-bold text-xl">
{t(I18nKey.EXPLORER$LABEL_DROP_FILES)}
</p>
</div>
)}
<div
@@ -176,7 +180,7 @@ function FileExplorer() {
>
{!isHidden && (
<div className="ml-1 text-neutral-300 font-bold text-sm">
Workspace
{t(I18nKey.EXPLORER$LABEL_WORKSPACE)}
</div>
)}
<ExplorerActions

View File

@@ -58,7 +58,7 @@ function SettingsModal({ isOpen, onOpenChange }: SettingsProps) {
setModels(await fetchModels());
setAgents(await fetchAgents());
} catch (error) {
toast.error("settings", "Failed to fetch models and agents");
toast.error("settings", t(I18nKey.CONFIGURATION$ERROR_FETCH_MODELS));
} finally {
setLoading(false);
}

View File

@@ -268,6 +268,46 @@
"en": "Please stop the agent before editing these settings.",
"de": "Bitte beenden Sie den Agenten vor der Bearbeitung der Einstellungen."
},
"CONFIGURATION$ERROR_FETCH_MODELS": {
"en": "Failed to fetch models and agents",
"zh-CN": "获取模型和智能体失败",
"de": "Fehler beim Abrufen der Modelle und Agenten"
},
"SESSION$SERVER_CONNECTED_MESSAGE": {
"en": "Connected to server",
"zh-CN": "已连接到服务器",
"de": "Verbindung zum Server hergestellt"
},
"SESSION$SESSION_HANDLING_ERROR_MESSAGE": {
"en": "Error handling message",
"zh-CN": "处理消息时发生错误",
"de": "Fehler beim Verarbeiten der Nachricht"
},
"SESSION$SESSION_CONNECTION_ERROR_MESSAGE": {
"en": "Error connecting to session",
"zh-CN": "连接到会话时发生错误",
"de": "Verbindung zur Sitzung fehlgeschlagen"
},
"SESSION$SOCKET_NOT_INITIALIZED_ERROR_MESSAGE": {
"en": "Socket not initialized",
"zh-CN": "Socket 未初始化",
"de": "Socket nicht initialisiert"
},
"EXPLORER$UPLOAD_ERROR_MESSAGE": {
"en": "Error uploading file",
"zh-CN": "上传文件时发生错误",
"de": "Fehler beim Hochladen der Datei"
},
"EXPLORER$LABEL_DROP_FILES": {
"en": "Drop files here",
"zh-CN": "将文件拖到这里",
"de": "Dateien hier ablegen"
},
"EXPLORER$LABEL_WORKSPACE": {
"en": "Workspace",
"zh-CN": "工作区",
"de": "Arbeitsbereich"
},
"LOAD_SESSION$MODAL_TITLE": {
"en": "Return to existing session?",
"de": "Zurück zu vorhandener Sitzung?",
@@ -357,6 +397,11 @@
"de": "Agent hat die Aufgabe erledigt.",
"zh-CN": "智能体已完成任务"
},
"CHAT_INTERFACE$AGENT_REJECTED_MESSAGE": {
"en": "Agent has rejected the task.",
"de": "Agent hat die Aufgabe abgelehnt.",
"zh-CN": "智能体拒绝任务"
},
"CHAT_INTERFACE$AGENT_ERROR_MESSAGE": {
"en": "Agent encountered an error.",
"de": "Agent ist auf einen Fehler gelaufen.",
@@ -394,6 +439,26 @@
"ar": "إرسال",
"fr": "Envoyer"
},
"CHAT_INTERFACE$CHAT_MESSAGE_COPIED": {
"en": "Message copied to clipboard",
"zh-CN": "消息已复制到剪贴板",
"de": "Nachricht in die Zwischenablage kopiert"
},
"CHAT_INTERFACE$CHAT_MESSAGE_COPY_FAILED": {
"en": "Failed to copy message to clipboard",
"zh-CN": "复制消息到剪贴板失败",
"de": "Nachricht konnte nicht in die Zwischenablage kopiert werden"
},
"CHAT_INTERFACE$TOOLTIP_COPY_MESSAGE": {
"en": "Copy message",
"zh-CN": "复制消息",
"de": "Nachricht kopieren"
},
"CHAT_INTERFACE$TOOLTIP_SEND_MESSAGE": {
"en": "Send message",
"zh-CN": "发送消息",
"de": "Nachricht senden"
},
"CHAT_INTERFACE$INITIAL_MESSAGE": {
"en": "Hi! I'm OpenDevin, an AI Software Engineer. What would you like to build with me today?",
"zh-CN": "你好!我是 OpenDevin一名 AI 软件工程师。今天想和我一起编写什么程序呢?",

View File

@@ -36,6 +36,12 @@ const messageActions = {
[ActionType.FINISH]: (message: ActionMessage) => {
store.dispatch(addAssistantMessage(message.message));
},
[ActionType.REJECT]: (message: ActionMessage) => {
store.dispatch(addAssistantMessage(message.message));
},
[ActionType.DELEGATE]: (message: ActionMessage) => {
store.dispatch(addAssistantMessage(message.message));
},
[ActionType.RUN]: (message: ActionMessage) => {
if (message.args.thought) {
store.dispatch(addAssistantMessage(message.args.thought));

View File

@@ -27,6 +27,12 @@ export function handleObservationMessage(message: ObservationMessage) {
case ObservationType.AGENT_STATE_CHANGED:
store.dispatch(changeAgentState(message.extras.agent_state));
break;
case ObservationType.DELEGATE:
// TODO: better UI for delegation result (#2309)
if (message.content) {
store.dispatch(addAssistantMessage(message.content));
}
break;
default:
store.dispatch(addAssistantMessage(message.message));
break;

View File

@@ -1,8 +1,12 @@
import i18next from "i18next";
import toast from "#/utils/toast";
import { handleAssistantMessage } from "./actions";
import { getToken, setToken, clearToken } from "./auth";
import ActionType from "#/types/ActionType";
import { getSettings } from "./settings";
import { I18nKey } from "#/i18n/declaration";
const translate = (key: I18nKey) => i18next.t(key);
class Session {
private static _socket: WebSocket | null = null;
@@ -66,10 +70,12 @@ class Session {
private static _setupSocket(): void {
if (!Session._socket) {
throw new Error("Socket is not initialized.");
throw new Error(
translate(I18nKey.SESSION$SOCKET_NOT_INITIALIZED_ERROR_MESSAGE),
);
}
Session._socket.onopen = (e) => {
toast.success("ws", "Connected to server.");
toast.success("ws", translate(I18nKey.SESSION$SERVER_CONNECTED_MESSAGE));
Session._connecting = false;
Session._initializeAgent();
Session.callbacks.open?.forEach((callback) => {
@@ -84,7 +90,10 @@ class Session {
Session._history.push(data);
} catch (err) {
// TODO: report the error
toast.error("ws", "Error handing message.");
toast.error(
"ws",
translate(I18nKey.SESSION$SESSION_HANDLING_ERROR_MESSAGE),
);
return;
}
if (data.error && data.error_code === 401) {
@@ -101,8 +110,10 @@ class Session {
};
Session._socket.onerror = () => {
const msg = "Connection failed. Retry...";
toast.error("ws", msg);
toast.error(
"ws",
translate(I18nKey.SESSION$SESSION_CONNECTION_ERROR_MESSAGE),
);
};
Session._socket.onclose = () => {
@@ -135,15 +146,19 @@ class Session {
return;
}
if (!Session.isConnected()) {
throw new Error("Not connected to server.");
throw new Error(
translate(I18nKey.SESSION$SESSION_CONNECTION_ERROR_MESSAGE),
);
}
if (Session.isConnected()) {
Session._socket?.send(message);
Session._history.push(JSON.parse(message));
} else {
const msg = "Connection failed. Retry...";
toast.error("ws", msg);
toast.error(
"ws",
translate(I18nKey.SESSION$SESSION_CONNECTION_ERROR_MESSAGE),
);
}
}

View File

@@ -26,6 +26,9 @@ enum ActionType {
// Interact with the browser instance.
BROWSE_INTERACTIVE = "browse_interactive",
// Delegate a (sub)task to another agent.
DELEGATE = "delegate",
// Searches long-term memory.
RECALL = "recall",
@@ -33,6 +36,9 @@ enum ActionType {
// use the finish action to stop working.
FINISH = "finish",
// Reject a request from user or another agent.
REJECT = "reject",
// Adds a task to the plan.
ADD_TASK = "add_task",

View File

@@ -6,6 +6,7 @@ enum AgentState {
PAUSED = "paused",
STOPPED = "stopped",
FINISHED = "finished",
REJECTED = "rejected",
ERROR = "error",
}

View File

@@ -19,6 +19,9 @@ enum ObservationType {
// Agent state has changed
AGENT_STATE_CHANGED = "agent_state_changed",
// Delegate result
DELEGATE = "delegate",
}
export default ObservationType;

View File

@@ -0,0 +1,9 @@
const beep = () => {
const snd = new Audio("/beep.wav");
snd.addEventListener("canplaythrough", () => snd.play());
snd.addEventListener("error", (e) =>
console.error("Audio file could not be loaded", e),
);
};
export default beep;

View File

@@ -281,7 +281,14 @@ class AgentController:
self.delegateAction = None
# update delegate result observation
obs: Observation = AgentDelegateObservation(outputs=outputs, content='')
# TODO: replace this with AI-generated summary (#2395)
formatted_output = ', '.join(
f'{key}: {value}' for key, value in outputs.items()
)
content = f'Delegate agent finishes task with {formatted_output}'
obs: Observation = AgentDelegateObservation(
outputs=outputs, content=content
)
self.event_stream.add_event(obs, EventSource.AGENT)
return

View File

@@ -78,7 +78,10 @@ class AgentRejectAction(Action):
@property
def message(self) -> str:
return 'Task is rejected by the agent.'
msg: str = 'Task is rejected by the agent.'
if 'reason' in self.outputs:
msg += ' Reason: ' + self.outputs['reason']
return msg
@dataclass

View File

@@ -17,11 +17,12 @@ def generate_dockerfile_content(base_image: str) -> str:
'RUN apt update && apt install -y openssh-server wget sudo net-tools iproute2\n'
'RUN mkdir -p -m0755 /var/run/sshd\n'
'RUN mkdir -p /opendevin && mkdir -p /opendevin/logs && chmod 777 /opendevin/logs\n'
'RUN wget "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"\n'
'RUN bash Miniforge3-$(uname)-$(uname -m).sh -b -p /opendevin/miniforge3\n'
'RUN bash -c ". /opendevin/miniforge3/etc/profile.d/conda.sh && conda config --set changeps1 False && conda config --append channels conda-forge"\n'
'RUN echo "export PATH=/opendevin/miniforge3/bin:$PATH" >> ~/.bashrc\n'
'RUN echo "export PATH=/opendevin/miniforge3/bin:$PATH" >> /opendevin/bash.bashrc\n'
'RUN { test -d /opendevin/miniforge3 && echo "/opendevin/miniforge3 already in base image"; } || { \\\n'
' wget "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" && \\\n'
' bash Miniforge3-$(uname)-$(uname -m).sh -b -p /opendevin/miniforge3 && \\\n'
' bash -c ". /opendevin/miniforge3/etc/profile.d/conda.sh && conda config --set changeps1 False && conda config --append channels conda-forge" && \\\n'
' echo "export PATH=/opendevin/miniforge3/bin:$PATH" >> ~/.bashrc && \\\n'
' echo "export PATH=/opendevin/miniforge3/bin:$PATH" >> /opendevin/bash.bashrc; }\n'
).strip()
return dockerfile_content

96
poetry.lock generated
View File

@@ -416,17 +416,17 @@ files = [
[[package]]
name = "boto3"
version = "1.34.131"
version = "1.34.133"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "boto3-1.34.131-py3-none-any.whl", hash = "sha256:05e388cb937e82be70bfd7eb0c84cf8011ff35cf582a593873ac21675268683b"},
{file = "boto3-1.34.131.tar.gz", hash = "sha256:dab8f72a6c4e62b4fd70da09e08a6b2a65ea2115b27dd63737142005776ef216"},
{file = "boto3-1.34.133-py3-none-any.whl", hash = "sha256:da7e78c03270be872ad78301892396ffea56647efcb2c3a8621ef46a905541ab"},
{file = "boto3-1.34.133.tar.gz", hash = "sha256:7071f8ce1f09113ca5630860fd590464e6325a4df55faae83c956225941016fc"},
]
[package.dependencies]
botocore = ">=1.34.131,<1.35.0"
botocore = ">=1.34.133,<1.35.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@@ -435,13 +435,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
version = "1.34.131"
version = "1.34.133"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
{file = "botocore-1.34.131-py3-none-any.whl", hash = "sha256:13b011d7b206ce00727dcee26548fa3b550db9046d5a0e90ac25a6e6c8fde6ef"},
{file = "botocore-1.34.131.tar.gz", hash = "sha256:502ddafe1d627fcf1e4c007c86454e5dd011dba7c58bd8e8a5368a79f3e387dc"},
{file = "botocore-1.34.133-py3-none-any.whl", hash = "sha256:f269dad8e17432d2527b97ed9f1fd30ec8dc705f8b818957170d1af484680ef2"},
{file = "botocore-1.34.133.tar.gz", hash = "sha256:5ea609aa4831a6589e32eef052a359ad8d7311733b4d86a9d35dab4bd3ec80ff"},
]
[package.dependencies]
@@ -2771,13 +2771,13 @@ types-tqdm = "*"
[[package]]
name = "litellm"
version = "1.40.25"
version = "1.40.27"
description = "Library to easily interface with LLM API providers"
optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [
{file = "litellm-1.40.25-py3-none-any.whl", hash = "sha256:94bff834ca92338223bed474fe614ab30a706681a596e1e38ea60fe8a574a7a8"},
{file = "litellm-1.40.25.tar.gz", hash = "sha256:437d515274b8cc901d527fbf14ce46599700225f4e1dae2d19b46b446e402b08"},
{file = "litellm-1.40.27-py3-none-any.whl", hash = "sha256:f6906e5260d784e7e31d579f5b28545e87517268cb96dd0dcaf31e4c5d34073f"},
{file = "litellm-1.40.27.tar.gz", hash = "sha256:a13a04168be5a8e52d43c34c2e657ca2521da61039ac39a17abc233a1875923f"},
]
[package.dependencies]
@@ -3823,38 +3823,38 @@ dill = ">=0.3.8"
[[package]]
name = "mypy"
version = "1.10.0"
version = "1.10.1"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
{file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
{file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
{file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
{file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
{file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
{file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
{file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
{file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
{file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
{file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
{file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
{file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
{file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
{file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
{file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
{file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
{file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
{file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
{file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
{file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
{file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
{file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
{file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
{file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
{file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
{file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
{file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"},
{file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"},
{file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"},
{file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"},
{file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"},
{file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"},
{file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"},
{file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"},
{file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"},
{file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"},
{file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"},
{file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"},
{file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"},
{file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"},
{file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"},
{file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"},
{file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"},
{file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"},
{file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"},
{file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"},
{file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"},
{file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"},
{file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"},
{file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"},
{file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"},
{file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"},
{file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"},
]
[package.dependencies]
@@ -4190,13 +4190,13 @@ sympy = "*"
[[package]]
name = "openai"
version = "1.35.3"
version = "1.35.4"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.7.1"
files = [
{file = "openai-1.35.3-py3-none-any.whl", hash = "sha256:7b26544cef80f125431c073ffab3811d2421fbb9e30d3bd5c2436aba00b042d5"},
{file = "openai-1.35.3.tar.gz", hash = "sha256:d6177087f150b381d49499be782d764213fdf638d391b29ca692b84dd675a389"},
{file = "openai-1.35.4-py3-none-any.whl", hash = "sha256:894b79c485fae2df3a6b68ceb570730e5a480c08bccc32a412cf3be2d4eb1384"},
{file = "openai-1.35.4.tar.gz", hash = "sha256:b58a0d6257c5c86e85b9b2f43e6eed04ada616560df9da0dca6697d06845e7c8"},
]
[package.dependencies]
@@ -5598,13 +5598,13 @@ files = [
[[package]]
name = "reportlab"
version = "4.2.0"
version = "4.2.2"
description = "The Reportlab Toolkit"
optional = false
python-versions = "<4,>=3.7"
files = [
{file = "reportlab-4.2.0-py3-none-any.whl", hash = "sha256:53630f9d25a7938def3e6a93d723b72a7a5921d34d23cf7a0930adeb2cb0e6c1"},
{file = "reportlab-4.2.0.tar.gz", hash = "sha256:474fb28d63431a5d47d75c90d580393050df7d491a09c7877df3291a2e9f6d0a"},
{file = "reportlab-4.2.2-py3-none-any.whl", hash = "sha256:927616931637e2f13e2ee3b3b6316d7a07803170e258621cff7d138bde17fbb5"},
{file = "reportlab-4.2.2.tar.gz", hash = "sha256:765eecbdd68491c56947e29c38b8b69b834ee5dbbdd2fb7409f08ebdebf04428"},
]
[package.dependencies]
@@ -6370,13 +6370,13 @@ mpmath = ">=1.1.0,<1.4.0"
[[package]]
name = "tenacity"
version = "8.4.1"
version = "8.4.2"
description = "Retry code until it succeeds"
optional = false
python-versions = ">=3.8"
files = [
{file = "tenacity-8.4.1-py3-none-any.whl", hash = "sha256:28522e692eda3e1b8f5e99c51464efcc0b9fc86933da92415168bc1c4e2308fa"},
{file = "tenacity-8.4.1.tar.gz", hash = "sha256:54b1412b878ddf7e1f1577cd49527bad8cdef32421bd599beac0c6c3f10582fd"},
{file = "tenacity-8.4.2-py3-none-any.whl", hash = "sha256:9e6f7cf7da729125c7437222f8a522279751cdfbe6b67bfe64f75d3a348661b2"},
{file = "tenacity-8.4.2.tar.gz", hash = "sha256:cd80a53a79336edba8489e767f729e4f391c896956b57140b5d7511a64bbd3ef"},
]
[package.extras]
@@ -7737,4 +7737,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "34a62a0dedfa4ccee243811f066f45d4c6633e510e101e27b4fd31f91fb379bd"
content-hash = "dba3c8c3812d657e413a57e3bd87ad6f80adadc08857948ff1fd6e1c62692ca7"

View File

@@ -32,7 +32,7 @@ boto3 = "*"
minio = "^7.2.7"
gevent = "^24.2.1"
pyarrow = "16.1.0" # transitive dependency, pinned here to avoid conflicts
tenacity = "^8.4.1"
tenacity = "^8.4.2"
zope-interface = "6.4.post2"
[tool.poetry.group.llama-index.dependencies]
@@ -46,7 +46,7 @@ llama-index-embeddings-ollama = "*"
[tool.poetry.group.dev.dependencies]
ruff = "0.4.10"
mypy = "1.10.0"
mypy = "1.10.1"
pre-commit = "3.7.1"
[tool.poetry.group.test.dependencies]

View File

@@ -70,7 +70,7 @@ as well as observations you've made. This only includes the MOST RECENT
actions and observations--more may have happened before that.
They are time-ordered, with your most recent action at the bottom.
[{"source": "user", "action": "message", "args": {"content": "Fix typos in bad.txt. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"source": "agent", "action": "delegate", "args": {"agent": "TypoFixerAgent", "inputs": {"task": "Fix typos in bad.txt"}, "thought": ""}}, {"source": "agent", "observation": "delegate", "content": "", "extras": {"outputs": {"summary": {"file": "bad.txt", "typos_fixed": [{"original": "typoo", "corrected": "typo"}, {"original": "mor", "corrected": "more"}]}}}}]
[[{"source": "user", "action": "message", "args": {"content": "Fix typos in bad.txt. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "TypoFixerAgent", "inputs": {"task": "Fix typos in bad.txt"}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "Delegate agent finishes task with summary: {'file': 'bad.txt', 'typos_fixed': [{'original': 'typoo', 'corrected': 'typo'}, {'original': 'mor', 'corrected': 'more'}]}", "extras": {"outputs": {"summary": {"file": "bad.txt", "typos_fixed": [{"original": "typoo", "corrected": "typo"}, {"original": "mor", "corrected": "more"}]}}}}]]
If the last item in the history is an error, you should try to fix it. If you
cannot fix it, call the `reject` action.

View File

@@ -70,7 +70,7 @@ as well as observations you've made. This only includes the MOST RECENT
actions and observations--more may have happened before that.
They are time-ordered, with your most recent action at the bottom.
[[{"source": "user", "action": "message", "args": {"content": "Write a git commit message for the current staging area. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "CommitWriterAgent", "inputs": {}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "", "extras": {"outputs": {"reason": "Not a valid git repository."}}}]]
[[{"source": "user", "action": "message", "args": {"content": "Write a git commit message for the current staging area. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "CommitWriterAgent", "inputs": {}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "Delegate agent finishes task with reason: Not a valid git repository.", "extras": {"outputs": {"reason": "Not a valid git repository."}}}]]
If the last item in the history is an error, you should try to fix it. If you
cannot fix it, call the `reject` action.