Compare commits

..

1 Commits

Author SHA1 Message Date
Alex Bäuerle c29a25658c feat: make use of the partial file tree fetching implemented in the backend 2024-04-18 16:05:06 -07:00
425 changed files with 4077 additions and 36379 deletions
+1 -1
View File
@@ -1 +1 @@
*.ipynb linguist-vendored
*.ipynb linguist-vendored
+41
View File
@@ -0,0 +1,41 @@
---
name: Bug Report
about: Report a problem with OpenDevin
title: ''
labels: 'bug'
assignees: ''
---
<!-- You MUST fill out this template. We will close issues that don't include enough information to reproduce -->
#### Describe the bug
<!-- a short description of the problem -->
#### Setup and configuration
**Current version**:
<!-- run `git log -n 1` to see this -->
```bash
```
**My operating system**:
<!-- tell us everything about your environment -->
**My environment vars and other configuration** (be sure to redact API keys):
```bash
```
**My model and agent** (you can see these settings in the UI):
* Model:
* Agent:
**Commands I ran to install and run OpenDevin**:
```
```
**Steps to Reproduce**:
1.
2.
3.
**Logs, error messages, and screenshots**:
#### Additional Context
-69
View File
@@ -1,69 +0,0 @@
name: Bug
description: Report a problem with OpenDevin
title: '[Bug]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: Thank you for taking the time to fill out this bug report. We greatly appreciate your effort to complete this template fully. Please provide as much information as possible to help us understand and address the issue effectively.
- type: checkboxes
attributes:
label: Is there an existing issue for the same bug?
description: Please check if an issue already exists for the bug you encountered.
options:
- label: I have checked the troubleshooting document at https://opendevin.github.io/OpenDevin/modules/usage/troubleshooting
required: true
- label: I have checked the existing issues.
required: true
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: Provide a short description of the problem.
validations:
required: true
- type: textarea
id: current-version
attributes:
label: Current Version
description: What version are you using? If you're running in docker, tell us the tag you're using (e.g. ghcr.io/opendevin/opendevin:0.3.1).
render: bash
validations:
required: true
- type: textarea
id: config
attributes:
label: Installation and Configuration
description: Please provide any commands you ran and any configuration (redacting API keys)
render: bash
validations:
required: true
- type: textarea
id: model-agent
attributes:
label: Model and Agent
description: What model and agent are you using? You can see these settings in the UI by clicking the settings wheel.
placeholder: |
- Model:
- Agent:
- type: textarea
id: repro-steps
attributes:
label: Reproduction Steps
description: Please list the steps to reproduce the issue.
placeholder: |
1.
2.
3.
- type: textarea
id: additional-context
attributes:
label: Logs, Errors, Screenshots, and Additional Context
description: Please add any additional context about the problem here.
+47
View File
@@ -0,0 +1,47 @@
name: Build & Run Tests
on: [push, pull_request]
jobs:
on-macos:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install & Start Docker
run: |
brew install colima docker
colima start
- name: Install and configure Poetry
uses: snok/install-poetry@v1
with:
version: latest
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest ./tests
on-linux:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python3 -
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest ./tests
-55
View File
@@ -1,55 +0,0 @@
name: Deploy Docs to GitHub Pages
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
name: Build Docusaurus
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
cache-dependency-path: docs/package-lock.json
- name: Install dependencies
run: npm ci
- name: Build website
run: npm run build
- name: Upload Build Artifact
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
with:
path: docs/build
deploy:
name: Deploy to GitHub Pages
needs: build
if: github.ref == 'refs/heads/main'
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
# Deploy to the github-pages environment
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
@@ -10,8 +10,8 @@ permissions:
issues: write
jobs:
dogfood:
if: github.event.label.name == 'solve-this'
open-devin:
if: github.event.label.name == 'dogfood-this'
runs-on: ubuntu-latest
container:
image: ghcr.io/opendevin/opendevin
@@ -43,7 +43,7 @@ jobs:
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SANDBOX_TYPE: exec
run: |
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
python ./opendevin/main.py -d "./" -i 50 -f task.txt -d $GITHUB_WORKSPACE
rm task.txt
- name: Setup Git, Create Branch, and Commit Changes
-21
View File
@@ -1,21 +0,0 @@
name: Run e2e test with dummy agent
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up environment
run: |
curl -sSL https://install.python-poetry.org | python3 -
poetry install --without evaluation
wget https://huggingface.co/BAAI/bge-small-en-v1.5/raw/main/1_Pooling/config.json -P /tmp/llama_index/models--BAAI--bge-small-en-v1.5/snapshots/5c38ec7c405ec4b44b94cc5a9bb96e735b38267a/1_Pooling/
- name: Run tests
run: |
poetry run python opendevin/main.py -t "do a flip" -m ollama/not-a-model -d ./workspace/ -c DummyAgent
+4 -45
View File
@@ -2,9 +2,6 @@ name: Publish Docker Image
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
inputs:
reason:
@@ -15,11 +12,7 @@ on:
jobs:
ghcr_build_and_push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
if: github.event_name == 'push' || github.event.inputs.reason != ''
strategy:
matrix:
image: ["app", "evaluation", "sandbox"]
@@ -35,42 +28,8 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@v3
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Log-in to ghcr.io
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and push ${{ matrix.image }}
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }} --push
- name: Build ${{ matrix.image }}
if: github.event.pull_request.head.repo.full_name != github.repository
run: |
./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }}
docker_build_success:
name: Docker Build Success
runs-on: ubuntu-latest
needs: ghcr_build_and_push
steps:
- run: echo Done!
run: ./containers/build.sh ${{ matrix.image }} --push
+2 -12
View File
@@ -1,14 +1,9 @@
name: Lint
on:
push:
branches:
- main
pull_request:
on: [push, pull_request]
jobs:
lint-frontend:
name: Lint frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -37,12 +32,7 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: 'pip'
- name: Install pre-commit
run: pip install pre-commit==3.7.0
- name: Run pre-commit hooks
run: |
pre-commit run \
--all-files \
--show-diff-on-failure \
--config ./dev_config/python/.pre-commit-config.yaml
run: pre-commit run --files opendevin/**/* agenthub/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
-69
View File
@@ -1,69 +0,0 @@
name: Use OpenDevin to Review Pull Request
on:
pull_request:
types: [synchronize, labeled]
permissions:
contents: write
pull-requests: write
jobs:
dogfood:
if: contains(github.event.pull_request.labels.*.name, 'review-this')
runs-on: ubuntu-latest
container:
image: ghcr.io/opendevin/opendevin
volumes:
- /var/run/docker.sock:/var/run/docker.sock
steps:
- name: install git, github cli
run: |
apt-get install -y git gh
git config --global --add safe.directory $PWD
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref }} # check out the target branch
- name: Download Diff
run: |
curl -O "${{ github.event.pull_request.diff_url }}" -L
- name: Write Task File
run: |
echo "Your coworker wants to apply a pull request to this project. Read and review ${{ github.event.pull_request.number }}.diff file. Create a review-${{ github.event.pull_request.number }}.txt and write your concise comments and suggestions there." > task.txt
echo "" >> task.txt
echo "Title" >> task.txt
echo "${{ github.event.pull_request.title }}" >> task.txt
echo "" >> task.txt
echo "Description" >> task.txt
echo "${{ github.event.pull_request.body }}" >> task.txt
echo "" >> task.txt
echo "Diff file is: ${{ github.event.pull_request.number }}.diff" >> task.txt
- name: Run OpenDevin
env:
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SANDBOX_TYPE: exec
run: |
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
rm task.txt
- name: Check if review file is non-empty
id: check_file
run: |
ls -la
if [[ -s review-${{ github.event.pull_request.number }}.txt ]]; then
echo "non_empty=true" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Create PR review if file is non-empty
env:
GH_TOKEN: ${{ github.token }}
if: steps.check_file.outputs.non_empty == 'true'
run: |
gh pr review ${{ github.event.pull_request.number }} --comment --body-file "review-${{ github.event.pull_request.number }}.txt"
@@ -1,72 +0,0 @@
name: Run Integration Tests
on:
push:
branches:
- main
pull_request:
jobs:
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]
agent: ["SWEAgent", "PlannerAgent", "MonologueAgent", "CodeActAgent"]
sandbox: ["ssh", "exec"]
include:
- agent: "MonologueAgent"
embedding-model: "local"
- agent: "MonologueAgent"
# sufficient to have one agent testing against local sandbox
sandbox: "local"
embedding-model: "local"
- agent: "SWEAgent"
embedding-model: "none"
- agent: "PlannerAgent"
embedding-model: "none"
- agent: "CodeActAgent"
embedding-model: "none"
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install
- name: Build Environment
run: make build
- name: Run Integration Tests
env:
SANDBOX_TYPE: ${{ matrix.sandbox }}
AGENT: ${{ matrix.agent }}
MAX_ITERATIONS: 10
LLM_EMBEDDING_MODEL: ${{ matrix.embedding-model }}
run: |
rm -rf workspace
mkdir workspace
WORKSPACE_BASE="$GITHUB_WORKSPACE/workspace" \
WORKSPACE_MOUNT_PATH="$GITHUB_WORKSPACE/workspace" \
poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml \
-s ./tests/integration
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test_matrix_success:
name: All Integration Tests Passed
runs-on: ubuntu-latest
needs: [integration-tests]
steps:
- run: echo Done!
+20
View File
@@ -0,0 +1,20 @@
name: Run Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Set up environment
run: |
curl -sSL https://install.python-poetry.org | python3 -
poetry install --without evaluation
- name: Run tests
run: |
poetry run pytest ./tests
-88
View File
@@ -1,88 +0,0 @@
name: Run Unit Tests
on:
push:
branches:
- main
pull_request:
jobs:
test-on-macos:
name: Test on macOS
runs-on: macos-13
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install
- name: Install & Start Docker
run: |
brew install colima docker
colima start
# For testcontainers to find the Colima socket
# https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running
sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml ./tests/unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test-on-linux:
name: Test on Linux
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install --without evaluation
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml ./tests/unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test_matrix_success:
name: All Mac/Linux Tests Passed
runs-on: ubuntu-latest
needs: [test-on-macos, test-on-linux]
steps:
- run: echo Done!
+1
View File
@@ -57,6 +57,7 @@ cover/
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
+2 -3
View File
@@ -85,6 +85,5 @@ Please refer to the README in each module:
- [mock server](./opendevin/mock/README.md)
## Tests
Please navigate to `tests` folder to see existing test suites.
At the moment, we have two kinds of tests: `unit` and `integration`. Please refer to the README for each test suite. These tests also run on CI to ensure quality of
the project.
TODO: make sure code pass the test before submit.
+1 -27
View File
@@ -12,20 +12,6 @@ This guide is for people working on OpenDevin and editing the source code.
Make sure you have all these dependencies installed before moving on to `make build`.
#### Develop without sudo access
If you want to develop without system admin/sudo access to upgrade/install `Python` and/or `NodeJs`, you can use `conda` or `mamba` to manage the packages for you:
```bash
# Download and install Mamba (a faster version of conda)
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh
# Install Python 3.11, nodejs, and poetry
mamba install python=3.11
mamba install conda-forge::nodejs
mamba install conda-forge::poetry
```
### 2. Build and Setup The Environment
- **Build the Project:** Begin by building the project, which includes setting up the environment and installing dependencies. This step ensures that OpenDevin is ready to run smoothly on your system.
@@ -44,7 +30,7 @@ To configure the LM of your choice, follow these steps:
```bash
make setup-config
```
This command will prompt you to enter the LLM API key, model name, and other variables ensuring that OpenDevin is tailored to your specific needs. Note that the model name will apply only when you run headless. If you use the UI, please set the model in the UI.
This command will prompt you to enter the LLM API key and model name, ensuring that OpenDevin is tailored to your specific needs.
**Note on Alternative Models:**
Some alternative models may prove more challenging to tame than others. Fear not, brave adventurer! We shall soon unveil LLM-specific documentation to guide you on your quest. And if you've already mastered the art of wielding a model other than OpenAI's GPT, we encourage you to [share your setup instructions with us](https://github.com/OpenDevin/OpenDevin/issues/417).
@@ -82,15 +68,3 @@ If you encounter any issues with the Language Model (LM) or you're simply curiou
```bash
make help
```
### 8. Testing
#### Unit tests
```bash
poetry run pytest ./tests/unit/test_sandbox.py
```
#### Integration tests
Please refer to [this README](./tests/integration/README.md) for details.
+18 -82
View File
@@ -1,4 +1,3 @@
SHELL=/bin/bash
# Makefile for OpenDevin project
# Variables
@@ -12,11 +11,11 @@ CONFIG_FILE = config.toml
PRECOMMIT_CONFIG_PATH = "./dev_config/python/.pre-commit-config.yaml"
# ANSI color codes
GREEN=$(shell tput -Txterm setaf 2)
YELLOW=$(shell tput -Txterm setaf 3)
RED=$(shell tput -Txterm setaf 1)
BLUE=$(shell tput -Txterm setaf 6)
RESET=$(shell tput -Txterm sgr0)
GREEN=\033[0;32m
YELLOW=\033[0;33m
RED=\033[0;31m
BLUE=\033[0;34m
RESET=\033[0m
# Build
build:
@@ -31,27 +30,12 @@ build:
check-dependencies:
@echo "$(YELLOW)Checking dependencies...$(RESET)"
@$(MAKE) -s check-system
@$(MAKE) -s check-python
@$(MAKE) -s check-npm
@$(MAKE) -s check-nodejs
@$(MAKE) -s check-docker
@$(MAKE) -s check-poetry
@echo "$(GREEN)Dependencies checked successfully.$(RESET)"
check-system:
@echo "$(YELLOW)Checking system...$(RESET)"
@if [ "$(shell uname)" = "Darwin" ]; then \
echo "$(BLUE)macOS detected.$(RESET)"; \
elif [ "$(shell uname)" = "Linux" ]; then \
echo "$(BLUE)Linux detected.$(RESET)"; \
elif [ "$$(uname -r | grep -i microsoft)" ]; then \
echo "$(BLUE)Windows Subsystem for Linux detected.$(RESET)"; \
else \
echo "$(RED)Unsupported system detected. Please use macOS, Linux, or Windows Subsystem for Linux (WSL).$(RESET)"; \
exit 1; \
fi
check-python:
@echo "$(YELLOW)Checking Python installation...$(RESET)"
@if command -v python3.11 > /dev/null; then \
@@ -70,22 +54,6 @@ check-npm:
exit 1; \
fi
check-nodejs:
@echo "$(YELLOW)Checking Node.js installation...$(RESET)"
@if command -v node > /dev/null; then \
NODE_VERSION=$(shell node --version | sed -E 's/v//g'); \
IFS='.' read -r -a NODE_VERSION_ARRAY <<< "$$NODE_VERSION"; \
if [ "$${NODE_VERSION_ARRAY[0]}" -gt 18 ] || ([ "$${NODE_VERSION_ARRAY[0]}" -eq 18 ] && [ "$${NODE_VERSION_ARRAY[1]}" -gt 17 ]) || ([ "$${NODE_VERSION_ARRAY[0]}" -eq 18 ] && [ "$${NODE_VERSION_ARRAY[1]}" -eq 17 ] && [ "$${NODE_VERSION_ARRAY[2]}" -ge 1 ]); then \
echo "$(BLUE)Node.js $$NODE_VERSION is already installed.$(RESET)"; \
else \
echo "$(RED)Node.js 18.17.1 or later is required. Please install Node.js 18.17.1 or later to continue.$(RESET)"; \
exit 1; \
fi; \
else \
echo "$(RED)Node.js is not installed. Please install Node.js to continue.$(RESET)"; \
exit 1; \
fi
check-docker:
@echo "$(YELLOW)Checking Docker installation...$(RESET)"
@if command -v docker > /dev/null; then \
@@ -98,16 +66,7 @@ check-docker:
check-poetry:
@echo "$(YELLOW)Checking Poetry installation...$(RESET)"
@if command -v poetry > /dev/null; then \
POETRY_VERSION=$(shell poetry --version 2>&1 | sed -E 's/Poetry \(version ([0-9]+\.[0-9]+\.[0-9]+)\)/\1/'); \
IFS='.' read -r -a POETRY_VERSION_ARRAY <<< "$$POETRY_VERSION"; \
if [ $${POETRY_VERSION_ARRAY[0]} -ge 1 ] && [ $${POETRY_VERSION_ARRAY[1]} -ge 8 ]; then \
echo "$(BLUE)$(shell poetry --version) is already installed.$(RESET)"; \
else \
echo "$(RED)Poetry 1.8 or later is required. You can install poetry by running the following command, then adding Poetry to your PATH:"; \
echo "$(RED) curl -sSL https://install.python-poetry.org | python3 -$(RESET)"; \
echo "$(RED)More detail here: https://python-poetry.org/docs/#installing-with-the-official-installer$(RESET)"; \
exit 1; \
fi; \
echo "$(BLUE)$(shell poetry --version) is already installed.$(RESET)"; \
else \
echo "$(RED)Poetry is not installed. You can install poetry by running the following command, then adding Poetry to your PATH:"; \
echo "$(RED) curl -sSL https://install.python-poetry.org | python3.11 -$(RESET)"; \
@@ -128,7 +87,6 @@ install-python-dependencies:
poetry run pip install chroma-hnswlib; \
fi
@poetry install --without evaluation
@poetry run playwright install --with-deps chromium
@echo "$(GREEN)Python dependencies installed successfully.$(RESET)"
install-frontend-dependencies:
@@ -150,7 +108,7 @@ install-precommit-hooks:
lint:
@echo "$(YELLOW)Running linters...$(RESET)"
@poetry run pre-commit run --all-files --show-diff-on-failure --config $(PRECOMMIT_CONFIG_PATH)
@poetry run pre-commit run --files opendevin/**/* agenthub/**/* --show-diff-on-failure --config $(PRECOMMIT_CONFIG_PATH)
build-frontend:
@echo "$(YELLOW)Building frontend...$(RESET)"
@@ -159,12 +117,12 @@ build-frontend:
# Start backend
start-backend:
@echo "$(YELLOW)Starting backend...$(RESET)"
@poetry run uvicorn opendevin.server.listen:app --port $(BACKEND_PORT) --reload --reload-exclude workspace/*
@poetry run uvicorn opendevin.server.listen:app --port $(BACKEND_PORT)
# Start frontend
start-frontend:
@echo "$(YELLOW)Starting frontend...$(RESET)"
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run start
@cd frontend && BACKEND_HOST=$(BACKEND_HOST) FRONTEND_PORT=$(FRONTEND_PORT) npm run start
# Run the app
run:
@@ -190,7 +148,7 @@ setup-config:
@echo "$(GREEN)Config.toml setup completed.$(RESET)"
setup-config-prompts:
@read -p "Enter your LLM Model name, used for running without UI. Set the model in the UI after you start the app. (see https://docs.litellm.ai/docs/providers for full list) [default: $(DEFAULT_MODEL)]: " llm_model; \
@read -p "Enter your LLM Model name (see https://docs.litellm.ai/docs/providers for full list) [default: $(DEFAULT_MODEL)]: " llm_model; \
llm_model=$${llm_model:-$(DEFAULT_MODEL)}; \
echo "LLM_MODEL=\"$$llm_model\"" > $(CONFIG_FILE).tmp
@@ -200,27 +158,17 @@ setup-config-prompts:
@read -p "Enter your LLM Base URL [mostly used for local LLMs, leave blank if not needed - example: http://localhost:5001/v1/]: " llm_base_url; \
if [[ ! -z "$$llm_base_url" ]]; then echo "LLM_BASE_URL=\"$$llm_base_url\"" >> $(CONFIG_FILE).tmp; fi
@echo "Enter your LLM Embedding Model"; \
echo "Choices are:"; \
echo " - openai"; \
echo " - azureopenai"; \
echo " - Embeddings available only with OllamaEmbedding:"; \
echo " - llama2"; \
echo " - mxbai-embed-large"; \
echo " - nomic-embed-text"; \
echo " - all-minilm"; \
echo " - stable-code"; \
echo " - Leave blank to default to 'BAAI/bge-small-en-v1.5' via huggingface"; \
read -p "> " llm_embedding_model; \
echo "LLM_EMBEDDING_MODEL=\"$$llm_embedding_model\"" >> $(CONFIG_FILE).tmp; \
if [ "$$llm_embedding_model" = "llama2" ] || [ "$$llm_embedding_model" = "mxbai-embed-large" ] || [ "$$llm_embedding_model" = "nomic-embed-text" ] || [ "$$llm_embedding_model" = "all-minilm" ] || [ "$$llm_embedding_model" = "stable-code" ]; then \
read -p "Enter the local model URL for the embedding model (will set LLM_EMBEDDING_BASE_URL): " llm_embedding_base_url; \
echo "LLM_EMBEDDING_BASE_URL=\"$$llm_embedding_base_url\"" >> $(CONFIG_FILE).tmp; \
@echo "Enter your LLM Embedding Model\nChoices are openai, azureopenai, llama2 or leave blank to default to 'BAAI/bge-small-en-v1.5' via huggingface"; \
read -p "> " llm_embedding_model; \
echo "LLM_EMBEDDING_MODEL=\"$$llm_embedding_model\"" >> $(CONFIG_FILE).tmp; \
if [ "$$llm_embedding_model" = "llama2" ]; then \
read -p "Enter the local model URL (will overwrite LLM_BASE_URL): " llm_base_url; \
echo "LLM_BASE_URL=\"$$llm_base_url\"" >> $(CONFIG_FILE).tmp; \
elif [ "$$llm_embedding_model" = "azureopenai" ]; then \
read -p "Enter the Azure endpoint URL (will overwrite LLM_BASE_URL): " llm_base_url; \
echo "LLM_BASE_URL=\"$$llm_base_url\"" >> $(CONFIG_FILE).tmp; \
read -p "Enter the Azure LLM Embedding Deployment Name: " llm_embedding_deployment_name; \
echo "LLM_EMBEDDING_DEPLOYMENT_NAME=\"$$llm_embedding_deployment_name\"" >> $(CONFIG_FILE).tmp; \
read -p "Enter the Azure LLM Deployment Name: " llm_deployment_name; \
echo "LLM_DEPLOYMENT_NAME=\"$$llm_deployment_name\"" >> $(CONFIG_FILE).tmp; \
read -p "Enter the Azure API Version: " llm_api_version; \
echo "LLM_API_VERSION=\"$$llm_api_version\"" >> $(CONFIG_FILE).tmp; \
fi
@@ -229,18 +177,6 @@ setup-config-prompts:
workspace_dir=$${workspace_dir:-$(DEFAULT_WORKSPACE_DIR)}; \
echo "WORKSPACE_BASE=\"$$workspace_dir\"" >> $(CONFIG_FILE).tmp
# Clean up all caches
clean:
@echo "$(YELLOW)Cleaning up caches...$(RESET)"
@rm -rf opendevin/.cache
@echo "$(GREEN)Caches cleaned up successfully.$(RESET)"
python-docs:
@echo "$(YELLOW)Generating documentation...$(RESET)"
@rm -rf docs/modules/python
@pydoc-markdown
@echo "$(GREEN)Documentation generated successfully.$(RESET)"
# Help
help:
@echo "$(BLUE)Usage: make [target]$(RESET)"
+149 -5
View File
@@ -1,5 +1,6 @@
<a name="readme-top"></a>
[English](README.md) | [中文](docs/README-zh.md)
<a name="readme-top"></a>
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
@@ -8,6 +9,8 @@
*** Thanks again! Now go create something AMAZING! :D
-->
<!-- PROJECT SHIELDS -->
<!--
*** I'm using markdown "reference style" links for readability.
@@ -30,15 +33,148 @@
<!-- PROJECT LOGO -->
<div align="center">
<img src="./docs/static/img/logo.png" alt="Logo" width="200" height="200">
<img src="./logo.png" alt="Logo" width="200" height="200">
<h1 align="center">OpenDevin: Code Less, Make More</h1>
</div>
<!-- TABLE OF CONTENTS -->
<details>
<summary>🗂️ Table of Contents</summary>
<ol>
<li><a href="#-mission">🎯 Mission</a></li>
<li><a href="#-what-is-devin">🤔 What is Devin?</a></li>
<li><a href="#-why-opendevin">🐚 Why OpenDevin?</a></li>
<li><a href="#-project-status">🚧 Project Status</a></li>
<a href="#-get-started">🚀 Get Started</a>
<ul>
<li><a href="#1-requirements">1. Requirements</a></li>
<li><a href="#2-build-and-setup">2. Build and Setup</a></li>
<li><a href="#3-run-the-application">3. Run the Application</a></li>
<li><a href="#4-individual-server-startup">4. Individual Server Startup</a></li>
<li><a href="#5-help">5. Help</a></li>
</ul>
</li>
<li><a href="#%EF%B8%8F-research-strategy">⭐️ Research Strategy</a></li>
<li><a href="#-how-to-contribute">🤝 How to Contribute</a></li>
<li><a href="#-join-our-community">🤖 Join Our Community</a></li>
<li><a href="#%EF%B8%8F-built-with">🛠️ Built With</a></li>
<li><a href="#-license">📜 License</a></li>
</ol>
</details>
## 🎯 Mission
[Project Demo Video](https://github.com/OpenDevin/OpenDevin/assets/38853559/71a472cc-df34-430c-8b1d-4d7286c807c9)
Welcome to OpenDevin, an open-source project aiming to replicate Devin, an autonomous AI software engineer who is capable of executing complex engineering tasks and collaborating actively with users on software development projects. This project aspires to replicate, enhance, and innovate upon Devin through the power of the open-source community.
To learn more and to use OpenDevin, check out our [documentation](https://opendevin.github.io/OpenDevin/).
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🤔 What is Devin?
Devin represents a cutting-edge autonomous agent designed to navigate the complexities of software engineering. It leverages a combination of tools such as a shell, code editor, and web browser, showcasing the untapped potential of LLMs in software development. Our goal is to explore and expand upon Devin's capabilities, identifying both its strengths and areas for improvement, to guide the progress of open code models.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🐚 Why OpenDevin?
The OpenDevin project is born out of a desire to replicate, enhance, and innovate beyond the original Devin model. By engaging the open-source community, we aim to tackle the challenges faced by Code LLMs in practical scenarios, producing works that significantly contribute to the community and pave the way for future advancements.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🚧 Project Status
OpenDevin is currently a work in progress, but you can already run the alpha version to see the end-to-end system in action. The project team is actively working on the following key milestones:
- **UI**: Developing a user-friendly interface, including a chat interface, a shell demonstrating commands, and a web browser.
- **Architecture**: Building a stable agent framework with a robust backend that can read, write, and run simple commands.
- **Agent Capabilities**: Enhancing the agent's abilities to generate bash scripts, run tests, and perform other software engineering tasks.
- **Evaluation**: Establishing a minimal evaluation pipeline that is consistent with Devin's evaluation criteria.
After completing the MVP, the team will focus on research in various areas, including foundation models, specialist capabilities, evaluation, and agent studies.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## ⚠️ Caveats and Warnings
* OpenDevin is still an alpha project. It is changing very quickly and is unstable. We are working on getting a stable release out in the coming weeks.
* OpenDevin will issue many prompts to the LLM you configure. Most of these LLMs cost money--be sure to set spending limits and monitor usage.
* OpenDevin runs `bash` commands within a Docker sandbox, so it should not affect your machine. But your workspace directory will be attached to that sandbox, and files in the directory may be modified or deleted.
* Our default Agent is currently the MonologueAgent, which has limited capabilities, but is fairly stable. We're working on other Agent implementations, including [SWE Agent](https://swe-agent.com/). You can [read about our current set of agents here](./docs/documentation/Agents.md).
## 🚀 Get Started
The easiest way to run OpenDevin is inside a Docker container.
To start the app, run these commands, replacing `$(pwd)/workspace` with the path to the code you want OpenDevin to work with.
```bash
# Your OpenAI API key, or any other LLM API key
export LLM_API_KEY="sk-..."
# The directory you want OpenDevin to modify. MUST be an absolute path!
export WORKSPACE_DIR=$(pwd)/workspace
docker run \
-e LLM_API_KEY \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_DIR \
-v $WORKSPACE_DIR:/opt/workspace_base \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 3000:3000 \
ghcr.io/opendevin/opendevin:0.3.1
```
You'll find opendevin running at `http://localhost:3000`.
If you want to use the (unstable!) bleeding edge, you can use `ghcr.io/opendevin/opendevin:main` as the image.
See [Development.md](Development.md) for instructions on running OpenDevin without Docker.
## 🤖 LLM Backends
OpenDevin can work with any LLM backend.
For a full list of the LM providers and models available, please consult the
[litellm documentation](https://docs.litellm.ai/docs/providers).
The `LLM_MODEL` environment variable controls which model is used in programmatic interactions,
but choosing a model in the OpenDevin UI will override this setting.
The following environment variables might be necessary for some LLMs:
* `LLM_API_KEY`
* `LLM_BASE_URL`
* `LLM_EMBEDDING_MODEL`
* `LLM_DEPLOYMENT_NAME`
* `LLM_API_VERSION`
**Note on Alternative Models:**
Some alternative models may prove more challenging to tame than others.
Fear not, brave adventurer! We shall soon unveil LLM-specific documentation to guide you on your quest.
And if you've already mastered the art of wielding a model other than OpenAI's GPT,
we encourage you to [share your setup instructions with us](https://github.com/OpenDevin/OpenDevin/issues/417).
There is also [documentation for running with local models using ollama](./docs/documentation/LOCAL_LLM_GUIDE.md).
## ⭐️ Research Strategy
Achieving full replication of production-grade applications with LLMs is a complex endeavor. Our strategy involves:
1. **Core Technical Research:** Focusing on foundational research to understand and improve the technical aspects of code generation and handling.
2. **Specialist Abilities:** Enhancing the effectiveness of core components through data curation, training methods, and more.
3. **Task Planning:** Developing capabilities for bug detection, codebase management, and optimization.
4. **Evaluation:** Establishing comprehensive evaluation metrics to better understand and improve our models.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
@@ -66,8 +202,8 @@ For details, please check [this document](./CONTRIBUTING.md).
Now we have both Slack workspace for the collaboration on building OpenDevin and Discord server for discussion about anything related, e.g., this project, LLM, agent, etc.
- [Slack workspace](https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw)
- [Discord server](https://discord.gg/mBuDGRzzES)
* [Slack workspace](https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw)
* [Discord server](https://discord.gg/mBuDGRzzES)
If you would love to contribute, feel free to join our community (note that now there is no need to fill in the [form](https://forms.gle/758d5p6Ve8r2nxxq6)). Let's simplify software engineering together!
@@ -75,6 +211,14 @@ If you would love to contribute, feel free to join our community (note that now
[![Star History Chart](https://api.star-history.com/svg?repos=OpenDevin/OpenDevin&type=Date)](https://star-history.com/#OpenDevin/OpenDevin&Date)
## 🛠️ Built With
OpenDevin is built using a combination of powerful frameworks and libraries, providing a robust foundation for its development. Here are the key technologies used in the project:
![FastAPI](https://img.shields.io/badge/FastAPI-black?style=for-the-badge) ![uvicorn](https://img.shields.io/badge/uvicorn-black?style=for-the-badge) ![LiteLLM](https://img.shields.io/badge/LiteLLM-black?style=for-the-badge) ![Docker](https://img.shields.io/badge/Docker-black?style=for-the-badge) ![Ruff](https://img.shields.io/badge/Ruff-black?style=for-the-badge) ![MyPy](https://img.shields.io/badge/MyPy-black?style=for-the-badge) ![LlamaIndex](https://img.shields.io/badge/LlamaIndex-black?style=for-the-badge) ![React](https://img.shields.io/badge/React-black?style=for-the-badge)
Please note that the selection of these technologies is in progress, and additional technologies may be added or existing ones may be removed as the project evolves. We strive to adopt the most suitable and efficient tools to enhance the capabilities of OpenDevin.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
-2
View File
@@ -26,7 +26,6 @@ The `state` contains:
Here is a list of available Actions, which can be returned by `agent.step()`:
- [`CmdRunAction`](../opendevin/action/bash.py) - Runs a command inside a sandboxed terminal
- [`CmdKillAction`](../opendevin/action/bash.py) - Kills a background command
- [`IPythonRunCellAction`](../opendevin/action/bash.py) - Execute a block of Python code interactively (in Jupyter notebook) and receives `CmdOutputObservation`. Requires setting up `jupyter` [plugin](../opendevin/sandbox/plugins) as a requirement.
- [`FileReadAction`](../opendevin/action/fileop.py) - Reads the content of a file
- [`FileWriteAction`](../opendevin/action/fileop.py) - Writes new content to a file
- [`BrowseURLAction`](../opendevin/action/browse.py) - Gets the content of a URL
@@ -34,7 +33,6 @@ Here is a list of available Actions, which can be returned by `agent.step()`:
- [`AddTaskAction`](../opendevin/action/tasks.py) - Adds a subtask to the plan
- [`ModifyTaskAction`](../opendevin/action/tasks.py) - Changes the state of a subtask
- [`AgentThinkAction`](../opendevin/action/agent.py) - A no-op that allows the agent to add plaintext to the history (as well as the chat log)
- [`AgentTalkAction`](../opendevin/action/agent.py) - A no-op that allows the agent to add plaintext to the history and talk to the user.
- [`AgentFinishAction`](../opendevin/action/agent.py) - Stops the control loop, allowing the user to enter a new task
You can use `action.to_dict()` and `action_from_dict` to serialize and deserialize actions.
-1
View File
@@ -1,5 +1,4 @@
from opendevin.agent import Agent
from .agent import SWEAgent
Agent.register('SWEAgent', SWEAgent)
+9 -9
View File
@@ -1,23 +1,23 @@
from typing import List
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.state import State
from opendevin.action import (
Action,
AgentThinkAction,
FileReadAction,
FileWriteAction,
)
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.observation import Observation
from opendevin.state import State
from .parser import parse_command
from .prompts import (
CONTEXT_PROMPT,
SYSTEM_MESSAGE,
STEP_PROMPT,
MEMORY_FORMAT,
NO_ACTION,
STEP_PROMPT,
SYSTEM_MESSAGE,
CONTEXT_PROMPT
)
@@ -38,7 +38,7 @@ class SWEAgent(Agent):
def _remember(self, action: Action, observation: Observation) -> None:
"""Agent has a limited memory of the few steps implemented as a queue"""
memory = MEMORY_FORMAT(action.to_memory(), observation.to_memory())
memory = MEMORY_FORMAT(action.to_dict(), observation.to_dict())
self.running_memory.append(memory)
def _think_act(self, messages: List[dict]) -> tuple[Action, str]:
@@ -75,7 +75,7 @@ class SWEAgent(Agent):
)
msgs = [
{'content': SYSTEM_MESSAGE, 'role': 'system'},
{'content': SYSTEM_MESSAGE, 'role': 'user'},
{'content': prompt, 'role': 'user'}
]
+8 -8
View File
@@ -1,17 +1,17 @@
import re
from opendevin.action import (
Action,
AgentEchoAction,
AgentFinishAction,
AgentThinkAction,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
BrowseURLAction,
AgentEchoAction,
AgentThinkAction,
)
from .prompts import COMMAND_USAGE, CUSTOM_DOCS
import re
from .prompts import CUSTOM_DOCS, COMMAND_USAGE
# commands: exit, read, write, browse, kill, search_file, search_dir
@@ -20,7 +20,7 @@ no_open_file_error = AgentEchoAction(
def invalid_error(cmd, docs):
return f"""ERROR:
return f'''ERROR:
Invalid command structure for
```
{cmd}
@@ -30,7 +30,7 @@ If so, try again by running only one of the commands:
Try again using this format:
{COMMAND_USAGE[docs]}
"""
'''
def get_action_from_string(command_string: str, path: str, line: int, thoughts: str = '') -> Action | None:
+18 -19
View File
@@ -1,7 +1,8 @@
from opendevin.parse_commands import parse_command_file
DEFAULT_COMMANDS_DICT = {
'exit': 'Executed when task is complete',
'read <file_name> [<start_line>] [<end_line>]': "Shows a given file's contents starting from <start_line> up to <end_line>. Default: start_line = 0, end_line = -1. By default the whole file will be read.",
'read <file_name> [<start_line>] [<end_line>]': 'Shows a given file\'s contents starting from <start_line> up to <end_line>. Default: start_line = 0, end_line = -1. By default the whole file will be read.',
'write <file> <changes> [<start_line>] [<end_line>]': 'Modifies a <file> by replacing the current lines between <start_line> and <end_line> with <changes>. Default start_line = 0 and end_line = -1. Calling this with no line args will replace the whole file.',
'browse <url>': 'Returns the text version of any url, this can be useful to look up documentation or finding issues on github',
'scroll_up': 'Takes no arguments. This will scroll up and show you the 100 lines above your current lines',
@@ -16,7 +17,7 @@ DEFAULT_COMMANDS_DICT = {
COMMAND_USAGE = {
'exit': 'Usage:\n```\nexit\n```\nExecuted when task is complete',
'read': "Args:\n<file_name> [<start_line>] [<end_line>]\nUsage:\n```\nread file.py\n```\nor\n```\nread example.py <start_line> <end_line>\n```\nShows a given file's contents starting from <start_line> up to <end_line>. Default: start_line = 0, end_line = -1. by default the whole file will be read.",
'read': 'Args:\n<file_name> [<start_line>] [<end_line>]\nUsage:\n```\nread file.py\n```\nor\n```\nread example.py <start_line> <end_line>\n```\nShows a given file\'s contents starting from <start_line> up to <end_line>. Default: start_line = 0, end_line = -1. by default the whole file will be read.',
'write': 'Args:\n<file> <changes> [<start_line>] [<end_line>]\nUsage:\n```\nwrite "def main():\n print("This is line one")" 0 2\n```\nModifies a <file> by replacing the current lines between <start_line> and <end_line> with <changes>. Default start_line = 0 and end_line = -1. Calling this with no line args will replace the whole file.',
'edit': 'Args:\n<start_line> <end_line> <changes>\nUsage:\n```\nedit 0 1 import pandas as pd\n```\nThis will modify the current file you are in with the changes you make between the line numbers you designate',
'goto': 'Args:\n<line_num>\nUsage:\n```\ngoto <line_num>\n```\nThis will show you the 100 lines below and including the line you specify within your current file.',
@@ -28,9 +29,7 @@ COMMAND_USAGE = {
DEFAULT_COMMANDS = '\n'.join(
[k + ' - ' + v for k, v in DEFAULT_COMMANDS_DICT.items()])
# from opendevin.parse_commands import parse_command_file
# USE parse_command_file(filepath) to get the custom commands
CUSTOM_DOCS = None
CUSTOM_DOCS = parse_command_file()
CUSTOM_COMMANDS = f"""Custom bash commands:
{CUSTOM_DOCS}
@@ -52,7 +51,7 @@ To modify the current file use 'edit'. To move through the current file use 'got
when using write and edit do not surround the code with any "" just write the code.
"""
GENERAL_GUIDELINES = """INSTRUCTIONS:
GENERAL_GUIDELINES = '''INSTRUCTIONS:
Now, you're going to solve this issue on your own. You can use any bash commands or custom commands you wish to complete your task. Edit all the files you need to and run any checks or tests that you want.
Remember, YOU CAN ONLY ENTER ONE COMMAND AT A TIME. You should always wait for feedback after every command.
When you're satisfied with all of the changes you've made, you can indicate that you are done by running the exit command.
@@ -69,9 +68,9 @@ IMPORTANT TIPS:
5. Understand your context: Always make sure to look at the currently open file and the current working directory. The currently open file might be in a different directory than the working directory.
6. Verify your edits: When editing files, it is easy to accidentally specify a wrong line number or to write code with incorrect indentation. Always check the code after you issue an edit to make sure that it reflects what you wanted to accomplish. If it didn't, issue another command to fix it.
7. Thoroughly test your solution: After making any changes to fix a bug, be sure to thoroughly test your solution to ensure the bug has been resolved. Re-run the bug reproduction script and verify that the issue has been addressed.
"""
'''
RESPONSE_FORMAT = """RESPONSE FORMAT:
RESPONSE_FORMAT = '''RESPONSE FORMAT:
This is the format of the response you will make in order to solve the current issue.
You will be given multiple iterations to complete this task so break it into steps and solve them one by one.
@@ -113,9 +112,9 @@ Action:
[ END FORMAT ]
Do not provide anything extra just your thought and action.
"""
'''
SYSTEM_MESSAGE = f"""SYSTEM INFO:
SYSTEM_MESSAGE = f'''SYSTEM INFO:
You am an autonomous coding agent, here to provide solutions for coding issues.
You have been designed to assist you with a wide range of programming tasks, from code editing and debugging to testing and deployment.
You have access to a variety of tools and commands that you can use to help you solve problems efficiently.
@@ -123,10 +122,10 @@ You have access to a variety of tools and commands that you can use to help you
{GENERAL_GUIDELINES}
{DOCUMENTATION}
""".strip()
'''.strip()
def NO_ACTION(latest): return f"""
def NO_ACTION(latest): return f'''
You did not include any action to take in your most recent output:
===== Output ======
@@ -141,20 +140,20 @@ This time, be sure to use the exact format below, replacing anything in <> with
{RESPONSE_FORMAT}
It is crucial you use the format provided as the output will be parsed automatically.
"""
'''
def file_info(file: str, line: int):
if file:
return f"""CURRENT WORKSPACE:
return f'''CURRENT WORKSPACE:
Open File: {file} on line {line}
You can use these commands with the current file:
Navigation: `scroll_up`, `scroll_down`, and `goto <line>`
Modification: `edit <start_line> <end_line> <changes>`
"""
'''
def STEP_PROMPT(task, file, line_num): return f"""
def STEP_PROMPT(task, file, line_num): return f'''
{RESPONSE_FORMAT}
You are currently trying to complete this task:
{task}
@@ -168,7 +167,7 @@ Be very strict about the formatting that you use and make sure you follow the gu
NEVER output multiple commands. ONLY take ONE STEP at a time.
When you have completed your task run the "exit" command.
Begin with your thought about the next step and then come up with an action to perform your thought.
""".strip()
'''.strip()
def unpack_dict(data: dict, restrict: list[str] = []):
@@ -185,13 +184,13 @@ def unpack_dict(data: dict, restrict: list[str] = []):
return '\n'.join(lines)
def MEMORY_FORMAT(act, obs): return f"""
def MEMORY_FORMAT(act, obs): return f'''
Previous Action:
{unpack_dict(act, ["content"])}
Output from Action:
{unpack_dict(obs)}
""".strip()
'''.strip()
def CONTEXT_PROMPT(memory, window):
+7 -30
View File
@@ -1,35 +1,12 @@
from dotenv import load_dotenv
from opendevin.agent import Agent
from .micro.agent import MicroAgent
from .micro.registry import all_microagents
load_dotenv()
from . import ( # noqa: E402
SWE_agent,
codeact_agent,
delegator_agent,
dummy_agent,
monologue_agent,
planner_agent,
)
# Import agents after environment variables are loaded
from . import monologue_agent # noqa: E402
from . import codeact_agent # noqa: E402
from . import planner_agent # noqa: E402
from . import SWE_agent # noqa: E402
__all__ = ['monologue_agent', 'codeact_agent',
'planner_agent', 'SWE_agent',
'delegator_agent',
'dummy_agent']
for agent in all_microagents.values():
name = agent['name']
prompt = agent['prompt']
anon_class = type(name, (MicroAgent,), {
'prompt': prompt,
'agent_definition': agent,
})
Agent.register(name, anon_class)
'planner_agent', 'SWE_agent']
-1
View File
@@ -1,5 +1,4 @@
from opendevin.agent import Agent
from .codeact_agent import CodeActAgent
Agent.register('CodeActAgent', CodeActAgent)
+66 -79
View File
@@ -1,37 +1,65 @@
import re
from typing import List, Mapping
from agenthub.codeact_agent.prompt import EXAMPLES, SYSTEM_MESSAGE
from opendevin.action import (
Action,
AgentEchoAction,
AgentFinishAction,
AgentTalkAction,
CmdRunAction,
IPythonRunCellAction,
NullAction,
)
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.observation import (
AgentMessageObservation,
CmdOutputObservation,
IPythonRunCellObservation,
UserMessageObservation,
)
from opendevin.sandbox.plugins import (
JupyterRequirement,
PluginRequirement,
SWEAgentCommandsRequirement,
)
from opendevin.parse_commands import parse_command_file
from opendevin.state import State
COMMAND_DOCS = parse_command_file()
COMMAND_SEGMENT = (
f"""
Apart from the standard bash commands, you can also use the following special commands:
{COMMAND_DOCS}
"""
if COMMAND_DOCS is not None
else ''
)
SYSTEM_MESSAGE = f"""You are a helpful assistant. You will be provided access (as root) to a bash shell to complete user-provided tasks.
You will be able to execute commands in the bash shell, interact with the file system, install packages, and receive the output of your commands.
DO NOT provide code in ```triple backticks```. Instead, you should execute bash command on behalf of the user by wrapping them with <execute> and </execute>.
For example:
You can list the files in the current directory by executing the following command:
<execute>ls</execute>
You can also install packages using pip:
<execute> pip install numpy </execute>
You can also write a block of code to a file:
<execute>
echo "import math
print(math.pi)" > math.py
</execute>
{COMMAND_SEGMENT}
When you are done, execute the following to close the shell and end the conversation:
<execute>exit</execute>
"""
INVALID_INPUT_MESSAGE = (
"I don't understand your input. \n"
'If you want to execute command, please use <execute> YOUR_COMMAND_HERE </execute>.\n'
'If you already completed the task, please exit the shell by generating: <execute> exit </execute>.'
)
def parse_response(response) -> str:
action = response.choices[0].message.content
for lang in ['bash', 'ipython']:
if f'<execute_{lang}>' in action and f'</execute_{lang}>' not in action:
action += f'</execute_{lang}>'
if '<execute>' in action and '</execute>' not in action:
action += '</execute>'
return action
@@ -41,21 +69,6 @@ class CodeActAgent(Agent):
The agent works by passing the model a list of action-observation pairs and prompting the model to take the next step.
"""
sandbox_plugins: List[PluginRequirement] = [JupyterRequirement(), SWEAgentCommandsRequirement()]
SUPPORTED_ACTIONS = (
CmdRunAction,
IPythonRunCellAction,
AgentEchoAction,
AgentTalkAction,
NullAction
)
SUPPORTED_OBSERVATIONS = (
AgentMessageObservation,
UserMessageObservation,
CmdOutputObservation,
IPythonRunCellObservation
)
def __init__(
self,
llm: LLM,
@@ -89,82 +102,56 @@ class CodeActAgent(Agent):
assert state.plan.main_goal, 'Expecting instruction to be set'
self.messages = [
{'role': 'system', 'content': SYSTEM_MESSAGE},
{
'role': 'user',
'content': (
f'Here is an example of how you can interact with the environment for task solving:\n{EXAMPLES}\n\n'
f"NOW, LET'S START!\n\n{state.plan.main_goal}"
)
},
{'role': 'user', 'content': state.plan.main_goal},
]
updated_info = state.updated_info
if updated_info:
for prev_action, obs in updated_info:
assert isinstance(
prev_action, self.SUPPORTED_ACTIONS
), f'{prev_action.__class__} is not supported (supported: {self.SUPPORTED_ACTIONS})'
# prev_action is already added to self.messages when returned
# handle observations
assert isinstance(
obs, self.SUPPORTED_OBSERVATIONS
), f'{obs.__class__} is not supported (supported: {self.SUPPORTED_OBSERVATIONS})'
if isinstance(obs, (AgentMessageObservation, UserMessageObservation)):
prev_action, (CmdRunAction, AgentEchoAction)
), 'Expecting CmdRunAction or AgentEchoAction for Action'
if isinstance(
obs, AgentMessageObservation
): # warning message from itself
self.messages.append(
{'role': 'user', 'content': obs.content})
# User wants to exit
if obs.content.strip() == '/exit':
return AgentFinishAction()
elif isinstance(obs, CmdOutputObservation):
content = 'OBSERVATION:\n' + obs.content
content += f'\n[Command {obs.command_id} finished with exit code {obs.exit_code}]]'
self.messages.append({'role': 'user', 'content': content})
elif isinstance(obs, IPythonRunCellObservation):
content = 'OBSERVATION:\n' + obs.content
# replace base64 images with a placeholder
splited = content.split('\n')
for i, line in enumerate(splited):
if '![image](data:image/png;base64,' in line:
splited[i] = '![image](data:image/png;base64, ...) already displayed to user'
content = '\n'.join(splited)
self.messages.append({'role': 'user', 'content': content})
else:
raise NotImplementedError(
f'Unknown observation type: {obs.__class__}'
)
response = self.llm.completion(
messages=self.messages,
stop=[
'</execute_ipython>',
'</execute_bash>',
],
stop=['</execute>'],
temperature=0.0
)
action_str: str = parse_response(response)
state.num_of_chars += sum(
len(message['content']) for message in self.messages
) + len(action_str)
state.num_of_chars += sum(len(message['content'])
for message in self.messages) + len(action_str)
self.messages.append({'role': 'assistant', 'content': action_str})
if bash_command := re.search(r'<execute_bash>(.*)</execute_bash>', action_str, re.DOTALL):
# remove the command from the action string to get thought
thought = action_str.replace(bash_command.group(0), '').strip()
command = re.search(r'<execute>(.*)</execute>', action_str, re.DOTALL)
if command is not None:
# a command was found
command_group = bash_command.group(1).strip()
command_group = command.group(1)
if command_group.strip() == 'exit':
return AgentFinishAction()
return CmdRunAction(command=command_group, thought=thought)
elif python_code := re.search(r'<execute_ipython>(.*)</execute_ipython>', action_str, re.DOTALL):
# a code block was found
code_group = python_code.group(1).strip()
thought = action_str.replace(python_code.group(0), '').strip()
return IPythonRunCellAction(code=code_group, thought=thought)
return CmdRunAction(command=command_group)
# # execute the code
# # TODO: does exit_code get loaded into Message?
# exit_code, observation = self.env.execute(command_group)
# self._history.append(Message(Role.ASSISTANT, observation))
else:
# We assume the LLM is GOOD enough that when it returns pure natural language
# it want to talk to the user
return AgentTalkAction(content=action_str)
# we could provide a error message for the model to continue similar to
# https://github.com/xingyaoww/mint-bench/blob/main/mint/envs/general_env.py#L18-L23
# observation = INVALID_INPUT_MESSAGE
# self._history.append(Message(Role.ASSISTANT, observation))
return AgentEchoAction(
content=INVALID_INPUT_MESSAGE
) # warning message to itself
def search_memory(self, query: str) -> List[str]:
raise NotImplementedError('Implement this abstract method')
-226
View File
@@ -1,226 +0,0 @@
from opendevin.sandbox.plugins import SWEAgentCommandsRequirement
_SWEAGENT_BASH_DOCS = '\n'.join(
filter(
lambda x: not x.startswith('submit'),
SWEAgentCommandsRequirement.documentation.split('\n')
)
)
# _SWEAGENT_BASH_DOCS content below:
"""
open <path> [<line_number>] - opens the file at the given path in the editor. If line_number is provided, the window will be move to include that line
goto <line_number> - moves the window to show <line_number>
scroll_down - moves the window down {WINDOW} lines
scroll_up - moves the window down {WINDOW} lines
create <filename> - creates and opens a new file with the given name
search_dir <search_term> [<dir>] - searches for search_term in all files in dir. If dir is not provided, searches in the current directory
search_file <search_term> [<file>] - searches for search_term in file. If file is not provided, searches in the current open file
find_file <file_name> [<dir>] - finds all files with the given name in dir. If dir is not provided, searches in the current directory
edit <start_line>:<end_line>
<replacement_text>
end_of_edit - replaces lines <start_line> through <end_line> (inclusive) with the given text in the open file. The replacement text is terminated by a line with only end_of_edit on it. All of the <replacement text> will be entered, so make sure your indentation is formatted properly. Python files will be checked for syntax errors after the edit. If the system detects a syntax error, the edit will not be executed. Simply try to edit the file again, but make sure to read the error message and modify the edit command you issue accordingly. Issuing the same command a second time will just lead to the same error message again.
"""
_COMMAND_DOCS = (
'\nApart from the standard bash commands, you can also use the following special commands in <execute_bash> environment:\n'
f'{_SWEAGENT_BASH_DOCS}'
"Please note that THE EDIT COMMAND REQUIRES PROPER INDENTATION. If you'd like to add the line ' print(x)' you must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run."
)
SYSTEM_MESSAGE = f"""A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.
The assistant can interact with an interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code should be enclosed using "<execute_ipython>" tag, for example:
<execute_ipython>
print("Hello World!")
</execute_ipython>
The assistant can execute bash commands on behalf of the user by wrapping them with <execute_bash> and </execute_bash>.
For example, you can list the files in the current directory by <execute_bash> ls </execute_bash>.
The assistant should attempt fewer things at a time instead of putting too much commands OR code in one "execute" block.
The assistant can install Python packages through bash by <execute_bash> pip install [package needed] </execute_bash> and should always import packages and define variables before starting to use them.
The assistant should stop <execute> and provide an answer when they have already obtained the answer from the execution result.
{_COMMAND_DOCS}
Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
The assistant's response should be concise, but do express their thoughts.
Try to include one of <execute_ipython> or <execute_bash> in each of your responses, unless it is a direct answer to a question OR a message to the user.
"""
EXAMPLES = """
--- START OF EXAMPLE ---
USER: Can you create a list of numbers from 1 to 10, and create a web page to display them at port 5000?
ASSISTANT:
Sure! Let me write the Python code for starting a web server and save it to a file `app.py`:
<execute_ipython>
CODE='''
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
numbers = list(range(1, 11))
return str(numbers)
if __name__ == '__main__':
app.run(port=5000)
'''
with open('app.py', 'w') as f:
f.write(CODE)
</execute_ipython>
USER:
Observation:
[Code executed successfully with no output]
ASSISTANT:
I have created a Python file `app.py` that will display a list of numbers from 1 to 10 when you run it. Let me run the Python file for you:
<execute_bash>
python3 app.py > server.log 2>&1 &
</execute_bash>
USER:
Observation:
[1] 121[1]+ Exit 1 python3 app.py > server.log 2>&1
ASSISTANT: Looks like the server is running with PID 121 then crashed. Let me check the server log:
<execute_bash>
cat server.log
</execute_bash>
USER:
Observation:
Traceback (most recent call last):
File "/workspace/app.py", line 2, in <module>
from flask import Flask
ModuleNotFoundError: No module named 'flask'
ASSISTANT:
It seems that Flask is not installed. Let me install Flask for you:
<execute_bash>
pip install flask
</execute_bash>
USER:
Observation:
Defaulting to user installation because normal site-packages is not writeable
Collecting flask
Using cached flask-3.0.3-py3-none-any.whl (101 kB)
Collecting blinker>=1.6.2
Using cached blinker-1.7.0-py3-none-any.whl (13 kB)
Collecting Werkzeug>=3.0.0
Using cached werkzeug-3.0.2-py3-none-any.whl (226 kB)
Collecting click>=8.1.3
Using cached click-8.1.7-py3-none-any.whl (97 kB)
Collecting itsdangerous>=2.1.2
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Requirement already satisfied: Jinja2>=3.1.2 in /home/opendevin/.local/lib/python3.10/site-packages (from flask) (3.1.3)
Requirement already satisfied: MarkupSafe>=2.0 in /home/opendevin/.local/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask) (2.1.5)
Installing collected packages: Werkzeug, itsdangerous, click, blinker, flask
Successfully installed Werkzeug-3.0.2 blinker-1.7.0 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0
ASSISTANT:
Now that Flask is installed, let me run the Python file again:
<execute_bash>
python3 app.py > server.log 2>&1 &
</execute_bash>
USER:
Observation:
[1] 124
ASSISTANT:
Let me check the server log again:
<execute_bash>
cat server.log
</execute_bash>
USER:
Observation:
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
ASSISTANT:
The server is running on port 5000 with PID 124. You can access the list of numbers by visiting http://127.0.0.1:5000. If you have any further questions, feel free to ask!
USER: Now kill the server, make it display the numbers in a table format.
ASSISTANT:
Sure! Let me stop the server first:
<execute_bash>
kill 124
</execute_bash>
USER:
Observation:
[1]+ Terminated python3 app.py > server.log 2>&1
ASSISTANT:
The server has been stopped. Let me open the Python file and modify it.
<execute_bash>
open app.py
</execute_bash>
USER:
[File: /workspace/app.py (11 lines total)]
1:
2:from flask import Flask
3:app = Flask(__name__)
4:
5:@app.route('/')
6:def index():
7: numbers = list(range(1, 11))
8: return str(numbers)
9:
10:if __name__ == '__main__':
11: app.run(port=5000)
ASSISTANT:
I should edit the file to display the numbers in a table format. Let me do that for you:
<execute_bash>
edit 8:8 <<EOF
return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
EOF
</execute_bash>
USER:
Observation:
[File: /workspace/app.py (11 lines total)]
1:
2:from flask import Flask
3:app = Flask(__name__)
4:
5:@app.route('/')
6:def index():
7: numbers = list(range(1, 11))
8: return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
9:
10:if __name__ == '__main__':
11: app.run(port=5000)
File updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.
ASSISTANT:
The file has been updated. Let me run the Python file again with the new changes:
<execute_bash>
python3 app.py > server.log 2>&1 &
</execute_bash>
USER:
Observation:
[1] 126
ASSISTANT:
The server is running on port 5000 with PID 126. You can access the list of numbers in a table format by visiting http://127.0.0.1:5000. Free free to let me know if you have any further requests!
--- END OF EXAMPLE ---
"""
INVALID_INPUT_MESSAGE = (
"I don't understand your input. \n"
'If you want to execute a bash command, please use <execute_bash> YOUR_COMMAND_HERE </execute_bash>.\n'
'If you want to execute a block of Python code, please use <execute_ipython> YOUR_COMMAND_HERE </execute_ipython>.\n'
)
-5
View File
@@ -1,5 +0,0 @@
from opendevin.agent import Agent
from .agent import DelegatorAgent
Agent.register('DelegatorAgent', DelegatorAgent)
-72
View File
@@ -1,72 +0,0 @@
from typing import List
from opendevin.action import Action, AgentDelegateAction, AgentFinishAction
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.observation import AgentDelegateObservation
from opendevin.state import State
class DelegatorAgent(Agent):
"""
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
"""
current_delegate: str = ''
def __init__(self, llm: LLM):
"""
Initialize the Delegator Agent with an LLM
Parameters:
- llm (LLM): The llm to be used by this agent
"""
super().__init__(llm)
def step(self, state: State) -> Action:
"""
Checks to see if current step is completed, returns AgentFinishAction if True.
Otherwise, creates a plan prompt and sends to model for inference, returning the result as the next action.
Parameters:
- state (State): The current state given the previous actions and observations
Returns:
- AgentFinishAction: If the last state was 'completed', 'verified', or 'abandoned'
- Action: The next action to take based on llm response
"""
if self.current_delegate == '':
self.current_delegate = 'study'
return AgentDelegateAction(agent='StudyRepoForTaskAgent', inputs={
'task': state.plan.main_goal
})
lastObservation = state.history[-1][1]
if not isinstance(lastObservation, AgentDelegateObservation):
raise Exception('Last observation is not an AgentDelegateObservation')
if self.current_delegate == 'study':
self.current_delegate = 'coder'
return AgentDelegateAction(agent='Coder', inputs={
'task': state.plan.main_goal,
'summary': lastObservation.outputs['summary'],
})
elif self.current_delegate == 'coder':
self.current_delegate = 'verifier'
return AgentDelegateAction(agent='Verifier', inputs={
'task': state.plan.main_goal,
})
elif self.current_delegate == 'verifier':
if 'completed' in lastObservation.outputs and lastObservation.outputs['completed']:
return AgentFinishAction()
else:
self.current_delegate = 'coder'
return AgentDelegateAction(agent='Coder', inputs={
'task': state.plan.main_goal,
'summary': lastObservation.outputs['summary'],
})
else:
raise Exception('Invalid delegate state')
def search_memory(self, query: str) -> List[str]:
return []
-5
View File
@@ -1,5 +0,0 @@
from opendevin.agent import Agent
from .agent import DummyAgent
Agent.register('DummyAgent', DummyAgent)
-118
View File
@@ -1,118 +0,0 @@
import time
from typing import List, TypedDict
from opendevin.action import (
Action,
AddTaskAction,
AgentFinishAction,
AgentRecallAction,
AgentThinkAction,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
ModifyTaskAction,
)
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.observation import (
AgentRecallObservation,
CmdOutputObservation,
FileReadObservation,
FileWriteObservation,
NullObservation,
Observation,
)
from opendevin.state import State
"""
FIXME: There are a few problems this surfaced
* FileWrites seem to add an unintended newline at the end of the file
* command_id is sometimes a number, sometimes a string
* Why isn't the output of the background command split between two steps?
* Browser not working
"""
ActionObs = TypedDict('ActionObs', {'action': Action, 'observations': List[Observation]})
BACKGROUND_CMD = 'echo "This is in the background" && sleep .1 && echo "This too"'
class DummyAgent(Agent):
"""
The DummyAgent is used for e2e testing. It just sends the same set of actions deterministically,
without making any LLM calls.
"""
def __init__(self, llm: LLM):
super().__init__(llm)
self.steps: List[ActionObs] = [{
'action': AddTaskAction(parent='0', goal='check the current directory'),
'observations': [NullObservation('')],
}, {
'action': AddTaskAction(parent='0.0', goal='run ls'),
'observations': [NullObservation('')],
}, {
'action': ModifyTaskAction(id='0.0', state='in_progress'),
'observations': [NullObservation('')],
}, {
'action': AgentThinkAction(thought='Time to get started!'),
'observations': [NullObservation('')],
}, {
'action': CmdRunAction(command='echo "foo"'),
'observations': [CmdOutputObservation('foo', command_id=-1, command='echo "foo"')],
}, {
'action': FileWriteAction(content='echo "Hello, World!"', path='hello.sh'),
'observations': [FileWriteObservation('', path='hello.sh')],
}, {
'action': FileReadAction(path='hello.sh'),
'observations': [FileReadObservation('echo "Hello, World!"\n', path='hello.sh')],
}, {
'action': CmdRunAction(command='bash hello.sh'),
'observations': [CmdOutputObservation('Hello, World!', command_id=-1, command='bash hello.sh')],
}, {
'action': CmdRunAction(command=BACKGROUND_CMD, background=True),
'observations': [
CmdOutputObservation('Background command started. To stop it, send a `kill` action with id 42', command_id='42', command=BACKGROUND_CMD), # type: ignore[arg-type]
CmdOutputObservation('This is in the background\nThis too\n', command_id='42', command=BACKGROUND_CMD), # type: ignore[arg-type]
]
}, {
'action': AgentRecallAction(query='who am I?'),
'observations': [
AgentRecallObservation('', memories=['I am a computer.']),
# CmdOutputObservation('This too\n', command_id='42', command=BACKGROUND_CMD),
],
}, {
'action': BrowseURLAction(url='https://google.com'),
'observations': [
# BrowserOutputObservation('<html></html>', url='https://google.com', screenshot=""),
],
}, {
'action': AgentFinishAction(),
'observations': [],
}]
def step(self, state: State) -> Action:
time.sleep(0.1)
if state.iteration > 0:
prev_step = self.steps[state.iteration - 1]
if 'observations' in prev_step:
expected_observations = prev_step['observations']
hist_start = len(state.history) - len(expected_observations)
for i in range(len(expected_observations)):
hist_obs = state.history[hist_start + i][1].to_dict()
expected_obs = expected_observations[i].to_dict()
if 'command_id' in hist_obs['extras'] and hist_obs['extras']['command_id'] != -1:
del hist_obs['extras']['command_id']
hist_obs['content'] = ''
if 'command_id' in expected_obs['extras'] and expected_obs['extras']['command_id'] != -1:
del expected_obs['extras']['command_id']
expected_obs['content'] = ''
if hist_obs != expected_obs:
print('\nactual', hist_obs)
print('\nexpect', expected_obs)
assert hist_obs == expected_obs, f'Expected observation {expected_obs}, got {hist_obs}'
return self.steps[state.iteration]['action']
def search_memory(self, query: str) -> List[str]:
return ['I am a computer.']
-14
View File
@@ -1,14 +0,0 @@
## Introduction
This package contains definitions of micro-agents. A micro-agent is defined
in the following structure:
```
[AgentName]
├── agent.yaml
└── prompt.md
```
Note that `prompt.md` could use jinja2 template syntax. During runtime, `prompt.md`
is loaded and rendered, and used together with `agent.yaml` to initialize a
micro-agent.
@@ -1,4 +0,0 @@
* `add_task` - add a task to your plan. Arguments:
* `parent` - the ID of the parent task
* `goal` - the goal of the task
* `subtasks` - a list of subtasks, each of which is a map with a `goal` key.
@@ -1,2 +0,0 @@
* `browse` - opens a web page. Arguments:
* `url` - the URL to open
@@ -1,3 +0,0 @@
* `delegate` - send a task to another agent from the list provided. Arguments:
* `agent` - the agent to which the task is delegated. MUST match a name in the list of agents provided.
* `inputs` - a dictionary of input parameters to the agent, as specified in the list
@@ -1,2 +0,0 @@
* `finish` - if ALL of your tasks and subtasks have been verified or abandoned, and you're absolutely certain that you've completed your task and have tested your work, use the finish action to stop working. Arguments:
* `outputs` - a dictionary representing the outputs of your task, if any
@@ -1,2 +0,0 @@
* `kill` - kills a background command
* `id` - the ID of the background command to kill
@@ -1,3 +0,0 @@
* `modify_task` - close a task. Arguments:
* `id` - the ID of the task to close
* `state` - set to 'in_progress' to start the task, 'completed' to finish it, 'verified' to assert that it was successful, 'abandoned' to give up on it permanently, or `open` to stop working on it for now.
@@ -1,2 +0,0 @@
* `read` - reads the content of a file. Arguments:
* `path` - the path of the file to read
@@ -1,3 +0,0 @@
* `run` - runs a command on the command line in a Linux shell. Arguments:
* `command` - the command to run
* `background` - if true, run the command in the background, so that other commands can be run concurrently. Useful for e.g. starting a server. You won't be able to see the logs. You don't need to end the command with `&`, just set this to true.
@@ -1,2 +0,0 @@
* `think` - make a plan, set a goal, or record your thoughts. Arguments:
* `thought` - the thought to record
@@ -1,3 +0,0 @@
* `write` - writes the content to a file. Arguments:
* `path` - the path of the file to write
* `content` - the content to write to the file
@@ -1,5 +0,0 @@
Your response MUST be in JSON format. It must be an object, and it must contain two fields:
* `action`, which is one of the actions specified here
* `args`, which is a map of key-value pairs, specifying the arguments for that action
You MUST NOT include any other text besides the JSON response
@@ -1,4 +0,0 @@
Here is a recent history of actions you've taken in service of this plan,
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.
-77
View File
@@ -1,77 +0,0 @@
import json
from typing import Dict, List
from jinja2 import BaseLoader, Environment
from opendevin.action import Action, action_from_dict
from opendevin.agent import Agent
from opendevin.exceptions import LLMOutputError
from opendevin.llm.llm import LLM
from opendevin.state import State
from .instructions import instructions
from .registry import all_microagents
def parse_response(orig_response: str) -> Action:
json_start = orig_response.find('{')
json_end = orig_response.rfind('}') + 1
response = orig_response[json_start:json_end]
try:
action_dict = json.loads(response)
except json.JSONDecodeError as e:
raise LLMOutputError(
'Invalid JSON in response. Please make sure the response is a valid JSON object'
) from e
action = action_from_dict(action_dict)
return action
def my_encoder(obj):
"""
Encodes objects as dictionaries
Parameters:
- obj (Object): An object that will be converted
Returns:
- dict: If the object can be converted it is returned in dict format
"""
if hasattr(obj, 'to_dict'):
return obj.to_dict()
def to_json(obj, **kwargs):
"""
Serialize an object to str format
"""
return json.dumps(obj, default=my_encoder, **kwargs)
class MicroAgent(Agent):
prompt = ''
agent_definition: Dict = {}
def __init__(self, llm: LLM):
super().__init__(llm)
if 'name' not in self.agent_definition:
raise ValueError('Agent definition must contain a name')
self.prompt_template = Environment(loader=BaseLoader).from_string(self.prompt)
self.delegates = all_microagents.copy()
del self.delegates[self.agent_definition['name']]
def step(self, state: State) -> Action:
prompt = self.prompt_template.render(
state=state,
instructions=instructions,
to_json=to_json,
delegates=self.delegates)
messages = [{'content': prompt, 'role': 'user'}]
resp = self.llm.completion(messages=messages)
action_resp = resp['choices'][0]['message']['content']
state.num_of_chars += len(prompt) + len(action_resp)
action = parse_response(action_resp)
return action
def search_memory(self, query: str) -> List[str]:
return []
-6
View File
@@ -1,6 +0,0 @@
name: CoderAgent
description: Given a particular task, and a detailed description of the codebase, accomplishes the task
inputs:
task: string
codebase_summary: string
outputs: {}
-27
View File
@@ -1,27 +0,0 @@
# Task
You are a software engineer. You've inherited an existing codebase, which you
need to modify to complete this task:
{{ state.plan.main_goal }}
{% if state.inputs.summary %}
Here's a summary of the codebase, as it relates to this task:
{{ state.inputs.summary }}
{% endif %}
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.write }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
Do NOT finish until you have completed the tasks.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}
-20
View File
@@ -1,20 +0,0 @@
import os
from typing import Dict
instructions: Dict = {}
base_dir = os.path.dirname(os.path.abspath(__file__)) + '/_instructions'
for root, dirs, files in os.walk(base_dir):
if len(files) == 0:
continue
rel_base = os.path.relpath(root, base_dir)
keys = rel_base.split('/')
obj = instructions
for key in keys:
if key not in obj:
obj[key] = {}
obj = obj[key]
for file in files:
without_ext = os.path.splitext(file)[0]
with open(os.path.join(root, file), 'r') as f:
obj[without_ext] = f.read()
-6
View File
@@ -1,6 +0,0 @@
name: ManagerAgent
description: Delegates tasks to microagents based on their area of expertise
generates: Action
inputs:
task: string
outputs: {}
-27
View File
@@ -1,27 +0,0 @@
# Task
You are in charge of accomplishing the following task:
{{ state.plan.main_goal }}
In order to accomplish this goal, you must delegate tasks to one or more agents, who
can do the actual work. A description of each agent is provided below. You MUST
select one of the delegates below to move towards accomplishing the task, and you MUST
provide the correct inputs for the delegate you select.
## Agents
{% for name, details in delegates.items() %}
### {{ name }}
{{ details.description }}
#### Inputs
{{ to_json(details.inputs) }}
{% endfor %}
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Available Actions
{{ instructions.actions.delegate }}
{{ instructions.actions.finish }}
## Format
{{ instructions.format.action }}
-25
View File
@@ -1,25 +0,0 @@
name: MathAgent
description: "Solves simple and complex math problems using python"
generates: Action
container: python:3.12.3-bookworm
inputs:
task: string
outputs:
answer: string
examples:
- inputs:
task: "What is 2 + 2?"
outputs:
answer: "4"
- inputs:
task: "What is the area of a circle with radius 7.324 inches?"
output:
answer: "168.518 square inches"
- inputs:
task: "What day of the week is 2099-01-01?"
outputs:
answer: "Saturday"
- inputs:
task: "What is the integral of sin(x^2) evaluated from -1 to 1?"
outputs:
answer: "0.603848"
-23
View File
@@ -1,23 +0,0 @@
# Task
You are a brilliant mathematician and programmer. You've been given the follwoing problem to solve:
{{ state.plan.main_goal }}
Please write a python script that solves this problem, and prints the answer to stdout.
ONLY print the answer to stdout, nothing else.
You should then run the python script with `python3`,
and call the `finish` action with `outputs.answer` set to the answer.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
If the last item in the history is an error, you should try to fix it.
## Available Actions
{{ instructions.actions.write }}
{{ instructions.actions.run }}
{{ instructions.actions.finish }}
## Format
{{ instructions.format.action }}
-6
View File
@@ -1,6 +0,0 @@
name: PostgresAgent
description: Writes and maintains PostgreSQL migrations
generates: Action
inputs:
task: string
outputs: {}
-24
View File
@@ -1,24 +0,0 @@
# Task
You are a database engineer. You are working on an existing Postgres project, and have been given
the following task:
{{ state.plan.main_goal }}
You must:
* Investigate the existing migrations to understand the current schema
* Write a new migration to accomplish the task above
* Test that the migrations work properly
## Actions
You may take any of the following actions:
{{ instructions.actions.think }}
{{ instructions.actions.read }}
{{ instructions.actions.write }}
{{ instructions.actions.run }}
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}
-25
View File
@@ -1,25 +0,0 @@
import os
import yaml
all_microagents = {}
for dir in os.listdir(os.path.dirname(__file__)):
base = os.path.dirname(__file__) + '/' + dir
if os.path.isfile(base):
continue
if dir.startswith('_'):
continue
promptFile = base + '/prompt.md'
agentFile = base + '/agent.yaml'
if not os.path.isfile(promptFile) or not os.path.isfile(agentFile):
raise Exception(
f'Missing prompt or agent file in {base}. Please create them.')
with open(promptFile, 'r') as f:
prompt = f.read()
with open(agentFile, 'r') as f:
agent = yaml.safe_load(f)
if 'name' not in agent:
raise Exception(f'Missing name in {agentFile}')
agent['prompt'] = prompt
all_microagents[agent['name']] = agent
-5
View File
@@ -1,5 +0,0 @@
name: RepoExplorerAgent
description: Generates a detailed summary of an existing codebase
inputs: {}
outputs:
summary: string
-26
View File
@@ -1,26 +0,0 @@
# Task
You are a software engineer. You've inherited an existing codebase, which you're
learning about for the first time. Your goal is to produce a detailed summary
of the codebase, including:
* The overall purpose of the project
* The directory structure
* The main components of the codebase
* How the components fit together
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
You should ONLY `run` commands that have no side-effects, like `ls` and `grep`.
Do NOT finish until you have a complete understanding of the codebase.
When you're done, put your summary into the output of the `finish` action.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}
@@ -1,6 +0,0 @@
name: StudyRepoForTaskAgent
description: Given a particular task, finds and describes all relevant parts of the codebase
inputs:
task: string
outputs:
summary: string
@@ -1,25 +0,0 @@
# Task
You are a software engineer. You've inherited an existing codebase, which you're
learning about for the first time. You need to study the codebase to find all
the information needed to complete this task:
{{ state.plan.main_goal }}
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
You must ONLY `run` commands that have no side-effects, like `ls` and `grep`.
Do NOT finish until you have a complete understanding of which parts of the
codebase are relevant to the task, including particular files, function, functions, and classes.
When you're done, put your summary in `outputs.summary` in the `finish` action.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}
-7
View File
@@ -1,7 +0,0 @@
name: VerifierAgent
description: Given a particular task, verifies that the task has been completed
inputs:
task: string
outputs:
completed: boolean
summary: string
-27
View File
@@ -1,27 +0,0 @@
# Task
You are a quality assurance engineer. Another engineer has made changes to the
codebase which are supposed to solve this task:
{{ state.plan.main_goal }}
Your goal is to verify that the changes are correct and bug-free.
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
You must ONLY `run` commands that have no side-effects, like `ls`, `grep`, and test scripts.
Do NOT finish until you know whether the task is complete and correct.
When you're done, add a `completed` boolean to the `outputs` of the `finish` action.
If `completed` is `false`, you MUST also provide a `summary` in the `outputs` of the `finish` action
explaining what the problem is.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}
-1
View File
@@ -1,5 +1,4 @@
from opendevin.agent import Agent
from .agent import MonologueAgent
Agent.register('MonologueAgent', MonologueAgent)
+47 -73
View File
@@ -1,36 +1,33 @@
from typing import List
from opendevin.agent import Agent
from opendevin.state import State
from opendevin.llm.llm import LLM
from opendevin.schema import ActionType, ObservationType
from opendevin.exceptions import AgentNoInstructionError
from opendevin.action import (
Action,
NullAction,
CmdRunAction,
FileWriteAction,
FileReadAction,
AgentRecallAction,
BrowseURLAction,
AgentThinkAction,
)
from opendevin.observation import (
Observation,
NullObservation,
CmdOutputObservation,
FileReadObservation,
AgentRecallObservation,
BrowserOutputObservation,
)
import agenthub.monologue_agent.utils.prompts as prompts
from agenthub.monologue_agent.utils.monologue import Monologue
from opendevin import config
from opendevin.action import (
Action,
AgentRecallAction,
AgentThinkAction,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
GitHubPushAction,
NullAction,
)
from opendevin.agent import Agent
from opendevin.exceptions import AgentNoInstructionError
from opendevin.llm.llm import LLM
from opendevin.observation import (
AgentRecallObservation,
BrowserOutputObservation,
CmdOutputObservation,
FileReadObservation,
NullObservation,
Observation,
)
from opendevin.schema import ActionType
from opendevin.schema.config import ConfigType
from opendevin.state import State
if config.get(ConfigType.AGENT_MEMORY_ENABLED):
from agenthub.monologue_agent.utils.memory import LongTermMemory
from agenthub.monologue_agent.utils.memory import LongTermMemory
MAX_MONOLOGUE_LENGTH = 20000
MAX_OUTPUT_LENGTH = 5000
@@ -71,17 +68,13 @@ INITIAL_THOUGHTS = [
'BROWSE google.com',
'<form><input type="text"></input><button type="submit"></button></form>',
'I can browse the web too!',
'If I have done some work and I want to push it to github, I can do that also!',
"Let's do it.",
'PUSH owner/repo branch',
'The repo was successfully pushed to https://github.com/owner/repo/branch',
'And once I have completed my task, I can use the finish action to stop working.',
"But I should only use the finish action when I'm absolutely certain that I've completed my task and have tested my work.",
'Very cool. Now to accomplish my task.',
"I'll need a strategy. And as I make progress, I'll need to keep refining that strategy. I'll need to set goals, and break them into sub-goals.",
'In between actions, I must always take some time to think, strategize, and set new goals. I should never take two actions in a row.',
"OK so my task is to $TASK. I haven't made any progress yet. Where should I start?",
'It seems like there might be an existing project here. I should probably start by running `pwd` and `ls` to orient myself.',
"It seems like there might be an existing project here. I should probably start by running `ls` to see what's here.",
]
@@ -93,8 +86,6 @@ class MonologueAgent(Agent):
"""
_initialized = False
monologue: Monologue
memory: 'LongTermMemory | None'
def __init__(self, llm: LLM):
"""
@@ -104,6 +95,8 @@ class MonologueAgent(Agent):
- llm (LLM): The llm to be used by this agent
"""
super().__init__(llm)
self.monologue = Monologue()
self.memory = LongTermMemory()
def _add_event(self, event: dict):
"""
@@ -126,17 +119,14 @@ class MonologueAgent(Agent):
)
self.monologue.add_event(event)
if self.memory is not None:
self.memory.add_event(event)
self.memory.add_event(event)
if self.monologue.get_total_length() > MAX_MONOLOGUE_LENGTH:
self.monologue.condense(self.llm)
def _initialize(self, task: str):
"""
Utilizes the INITIAL_THOUGHTS list to give the agent a context for it's capabilities
and how to navigate the WORKSPACE_MOUNT_PATH_IN_SANDBOX in `config` (e.g., /workspace by default).
Utilizes the INITIAL_THOUGHTS list to give the agent a context for it's capabilities and how to navigate the /workspace.
Short circuited to return when already initialized.
Will execute again when called after reset.
Parameters:
- task (str): The initial goal statement provided by the user
@@ -150,43 +140,35 @@ class MonologueAgent(Agent):
if task is None or task == '':
raise AgentNoInstructionError()
self.monologue = Monologue()
if config.get(ConfigType.AGENT_MEMORY_ENABLED):
self.memory = LongTermMemory()
else:
self.memory = None
self.memory = LongTermMemory()
self._add_initial_thoughts(task)
self._initialized = True
def _add_initial_thoughts(self, task):
previous_action = ''
output_type = ''
for thought in INITIAL_THOUGHTS:
thought = thought.replace('$TASK', task)
if previous_action != '':
if output_type != '':
observation: Observation = NullObservation(content='')
if previous_action in {ActionType.RUN, ActionType.PUSH}:
if output_type == ObservationType.RUN:
observation = CmdOutputObservation(
content=thought, command_id=0, command=''
)
elif previous_action == ActionType.READ:
elif output_type == ObservationType.READ:
observation = FileReadObservation(content=thought, path='')
elif previous_action == ActionType.RECALL:
elif output_type == ObservationType.RECALL:
observation = AgentRecallObservation(
content=thought, memories=[])
elif previous_action == ActionType.BROWSE:
elif output_type == ObservationType.BROWSE:
observation = BrowserOutputObservation(
content=thought, url='', screenshot=''
)
self._add_event(observation.to_memory())
previous_action = ''
self._add_event(observation.to_dict())
output_type = ''
else:
action: Action = NullAction()
if thought.startswith('RUN'):
command = thought.split('RUN ')[1]
action = CmdRunAction(command)
previous_action = ActionType.RUN
output_type = ActionType.RUN
elif thought.startswith('WRITE'):
parts = thought.split('WRITE ')[1].split(' > ')
path = parts[1]
@@ -195,23 +177,19 @@ class MonologueAgent(Agent):
elif thought.startswith('READ'):
path = thought.split('READ ')[1]
action = FileReadAction(path=path)
previous_action = ActionType.READ
output_type = ActionType.READ
elif thought.startswith('RECALL'):
query = thought.split('RECALL ')[1]
action = AgentRecallAction(query=query)
previous_action = ActionType.RECALL
output_type = ActionType.RECALL
elif thought.startswith('BROWSE'):
url = thought.split('BROWSE ')[1]
action = BrowseURLAction(url=url)
previous_action = ActionType.BROWSE
elif thought.startswith('PUSH'):
owner_repo, branch = thought.split('PUSH ')[1].split(' ')
owner, repo = owner_repo.split('/')
action = GitHubPushAction(owner=owner, repo=repo, branch=branch)
previous_action = ActionType.PUSH
output_type = ActionType.BROWSE
else:
action = AgentThinkAction(thought=thought)
self._add_event(action.to_memory())
self._initialized = True
def step(self, state: State) -> Action:
"""
@@ -226,7 +204,7 @@ class MonologueAgent(Agent):
self._initialize(state.plan.main_goal)
for prev_action, obs in state.updated_info:
self._add_event(prev_action.to_memory())
self._add_event(obs.to_memory())
self._add_event(obs.to_dict())
state.updated_info = []
@@ -254,12 +232,8 @@ class MonologueAgent(Agent):
Returns:
- List[str]: A list of top 10 text results that matched the query
"""
if self.memory is None:
return []
return self.memory.search(query)
def reset(self) -> None:
super().reset()
# Reset the initial monologue and memory
self._initialized = False
self.monologue = Monologue()
-1
View File
@@ -1,5 +1,4 @@
import json
from json_repair import repair_json
+14 -68
View File
@@ -1,90 +1,42 @@
import threading
from threading import Thread
import chromadb
import llama_index.embeddings.openai.base as llama_openai
from llama_index.core import Document, VectorStoreIndex
from llama_index.core import Document
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core import VectorStoreIndex
from llama_index.vector_stores.chroma import ChromaVectorStore
from openai._exceptions import APIConnectionError, InternalServerError, RateLimitError
from tenacity import (
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from opendevin import config
from opendevin.logger import opendevin_logger as logger
from opendevin.schema.config import ConfigType
from . import json
num_retries = config.get(ConfigType.LLM_NUM_RETRIES)
retry_min_wait = config.get(ConfigType.LLM_RETRY_MIN_WAIT)
retry_max_wait = config.get(ConfigType.LLM_RETRY_MAX_WAIT)
# llama-index includes a retry decorator around openai.get_embeddings() function
# it is initialized with hard-coded values and errors
# this non-customizable behavior is creating issues when it's retrying faster than providers' rate limits
# this block attempts to banish it and replace it with our decorator, to allow users to set their own limits
if hasattr(llama_openai.get_embeddings, '__wrapped__'):
original_get_embeddings = llama_openai.get_embeddings.__wrapped__
else:
logger.warning('Cannot set custom retry limits.') # warn
num_retries = 1
original_get_embeddings = llama_openai.get_embeddings
def attempt_on_error(retry_state):
logger.error(f'{retry_state.outcome.exception()}. Attempt #{retry_state.attempt_number} | You can customize these settings in the configuration.', exc_info=False)
return True
@retry(reraise=True,
stop=stop_after_attempt(num_retries),
wait=wait_random_exponential(min=retry_min_wait, max=retry_max_wait),
retry=retry_if_exception_type((RateLimitError, APIConnectionError, InternalServerError)),
after=attempt_on_error)
def wrapper_get_embeddings(*args, **kwargs):
return original_get_embeddings(*args, **kwargs)
llama_openai.get_embeddings = wrapper_get_embeddings
embedding_strategy = config.get(ConfigType.LLM_EMBEDDING_MODEL)
embedding_strategy = config.get('LLM_EMBEDDING_MODEL')
# TODO: More embeddings: https://docs.llamaindex.ai/en/stable/examples/embeddings/OpenAI/
# There's probably a more programmatic way to do this.
supported_ollama_embed_models = ['llama2', 'mxbai-embed-large', 'nomic-embed-text', 'all-minilm', 'stable-code']
if embedding_strategy in supported_ollama_embed_models:
if embedding_strategy == 'llama2':
from llama_index.embeddings.ollama import OllamaEmbedding
embed_model = OllamaEmbedding(
model_name=embedding_strategy,
base_url=config.get(ConfigType.LLM_EMBEDDING_BASE_URL, required=True),
model_name='llama2',
base_url=config.get('LLM_BASE_URL', required=True),
ollama_additional_kwargs={'mirostat': 0},
)
elif embedding_strategy == 'openai':
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding(
model='text-embedding-ada-002',
api_key=config.get(ConfigType.LLM_API_KEY, required=True)
api_key=config.get('LLM_API_KEY', required=True)
)
elif embedding_strategy == 'azureopenai':
# Need to instruct to set these env variables in documentation
from llama_index.embeddings.azure_openai import AzureOpenAIEmbedding
embed_model = AzureOpenAIEmbedding(
model='text-embedding-ada-002',
deployment_name=config.get(ConfigType.LLM_EMBEDDING_DEPLOYMENT_NAME, required=True),
api_key=config.get(ConfigType.LLM_API_KEY, required=True),
azure_endpoint=config.get(ConfigType.LLM_BASE_URL, required=True),
api_version=config.get(ConfigType.LLM_API_VERSION, required=True),
deployment_name=config.get('LLM_DEPLOYMENT_NAME', required=True),
api_key=config.get('LLM_API_KEY', required=True),
azure_endpoint=config.get('LLM_BASE_URL', required=True),
api_version=config.get('LLM_API_VERSION', required=True),
)
elif (embedding_strategy is not None) and (embedding_strategy.lower() == 'none'):
# TODO: this works but is not elegant enough. The incentive is when
# monologue agent is not used, there is no reason we need to initialize an
# embedding model
embed_model = None
else:
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
embed_model = HuggingFaceEmbedding(
@@ -92,9 +44,6 @@ else:
)
sema = threading.Semaphore(value=config.get(ConfigType.AGENT_MEMORY_MAX_THREADS))
class LongTermMemory:
"""
Responsible for storing information that the agent can call on later for better insights and context.
@@ -111,7 +60,6 @@ class LongTermMemory:
self.index = VectorStoreIndex.from_vector_store(
vector_store, embed_model=embed_model)
self.thought_idx = 0
self._add_threads = []
def add_event(self, event: dict):
"""
@@ -139,13 +87,11 @@ class LongTermMemory:
)
self.thought_idx += 1
logger.debug('Adding %s event to memory: %d', t, self.thought_idx)
thread = threading.Thread(target=self._add_doc, args=(doc,))
self._add_threads.append(thread)
thread = Thread(target=self._add_doc, args=(doc,))
thread.start() # We add the doc concurrently so we don't have to wait ~500ms for the insert
def _add_doc(self, doc):
with sema:
self.index.insert(doc)
self.index.insert(doc)
def search(self, query: str, k: int = 10):
"""
+7 -9
View File
@@ -1,9 +1,9 @@
import traceback
from opendevin.llm.llm import LLM
from opendevin.exceptions import AgentEventTypeError
import agenthub.monologue_agent.utils.json as json
import agenthub.monologue_agent.utils.prompts as prompts
from opendevin.exceptions import AgentEventTypeError
from opendevin.llm.llm import LLM
from opendevin.logger import opendevin_logger as logger
class Monologue:
@@ -53,7 +53,7 @@ class Monologue:
try:
total_length += len(json.dumps(t))
except TypeError as e:
logger.error('Error serializing thought: %s', str(e), exc_info=False)
print(f'Error serializing thought: {e}')
return total_length
def condense(self, llm: LLM):
@@ -64,7 +64,7 @@ class Monologue:
- llm (LLM): llm to be used for summarization
Raises:
- Exception: the same exception as it got from the llm or processing the response
- RunTimeError: When the condensing process fails for any reason
"""
try:
@@ -74,7 +74,5 @@ class Monologue:
summary_resp = resp['choices'][0]['message']['content']
self.thoughts = prompts.parse_summary_response(summary_resp)
except Exception as e:
logger.error('Error condensing thoughts: %s', str(e), exc_info=False)
# TODO If the llm fails with ContextWindowExceededError, we can try to condense the monologue chunk by chunk
raise
traceback.print_exc()
raise RuntimeError(f'Error condensing thoughts: {e}')
+16 -34
View File
@@ -1,19 +1,18 @@
import re
from json import JSONDecodeError
from typing import List
from opendevin import config
from . import json
from json import JSONDecodeError
import re
from opendevin.action import (
Action,
action_from_dict,
Action,
)
from opendevin.exceptions import LLMOutputError
from opendevin.observation import (
CmdOutputObservation,
)
from opendevin.schema.config import ConfigType
from . import json
from opendevin.exceptions import LLMOutputError
ACTION_PROMPT = """
You're a thoughtful robot. Your main task is this:
@@ -27,8 +26,8 @@ This is your internal monologue, in JSON format:
Your most recent thought is at the bottom of that monologue. Continue your train of thought.
What is your next single thought or action? Your response must be in JSON format.
It must be a single object, and it must contain two fields:
What is your next thought or action? Your response must be in JSON format.
It must be an object, and it must contain two fields:
* `action`, which is one of the actions below
* `args`, which is a map of key-value pairs, specifying the arguments for that action
@@ -45,10 +44,6 @@ Here are the possible actions:
* `id` - the ID of the background command to kill
* `browse` - opens a web page. Arguments:
* `url` - the URL to open
* `push` - Push a branch from the current repo to github:
* `owner` - the owner of the repo to push to
* `repo` - the name of the repo to push to
* `branch` - the name of the branch to push
* `recall` - recalls a past memory. Arguments:
* `query` - the query to search for
* `think` - make a plan, set a goal, or record your thoughts. Arguments:
@@ -57,20 +52,16 @@ Here are the possible actions:
%(background_commands)s
You MUST take time to think in between read, write, run, browse, push, and recall actions.
You MUST take time to think in between read, write, run, browse, and recall actions.
You should never act twice in a row without thinking. But if your last several
actions are all "think" actions, you should consider taking a different action.
Notes:
* you are logged in as %(user)s, but sudo will always work without a password.
* all non-background commands will be forcibly stopped if they remain running for over %(timeout)s seconds.
* your environment is Debian Linux. You can install software with `sudo apt-get`, but remember to use -y.
* your environment is Debian Linux. You can install software with `apt`
* your working directory will not change, even if you run `cd`. All commands will be run in the `/workspace` directory.
* don't run interactive commands, or commands that don't return (e.g. `node server.js`). You may run commands in the background (e.g. `node server.js &`)
* don't run interactive text editors (e.g. `nano` or 'vim'), instead use the 'write' or 'read' action.
* don't run gui applications (e.g. software IDEs (like vs code or codium), web browsers (like firefox or chromium), or other complex software packages). Use non-interactive cli applications, or special actions instead.
* whenever an action fails, always `think` about why it may have happened before acting again.
What is your next single thought or action? Again, you must reply with JSON, and only with JSON. You must respond with exactly one 'action' object.
What is your next thought or action? Again, you must reply with JSON, and only with JSON.
%(hint)s
"""
@@ -149,16 +140,11 @@ def get_request_action_prompt(
)
bg_commands_message += '\nYou can end any process by sending a `kill` action with the numerical `id` above.'
user = 'opendevin' if config.get(ConfigType.RUN_AS_DEVIN) else 'root'
return ACTION_PROMPT % {
'task': task,
'monologue': json.dumps(thoughts, indent=2),
'background_commands': bg_commands_message,
'hint': hint,
'user': user,
'timeout': config.get(ConfigType.SANDBOX_TIMEOUT),
'WORKSPACE_MOUNT_PATH_IN_SANDBOX': config.get(ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX),
}
@@ -184,14 +170,10 @@ def parse_action_response(response: str) -> Action:
return len(match[2]) if match[1] == 'think' else 130 # Crudely rank multiple responses by length
try:
action_dict = json.loads(max(response_json_matches, key=rank)[0]) # Use the highest ranked response
except (ValueError, JSONDecodeError):
except ValueError as e:
raise LLMOutputError(
'Invalid JSON, the response must be well-formed JSON as specified in the prompt.'
)
except (ValueError, TypeError):
raise LLMOutputError(
'Invalid JSON, the response must be well-formed JSON as specified in the prompt.'
)
"Output from the LLM isn't properly formatted. The model may be misconfigured."
) from e
if 'content' in action_dict:
# The LLM gets confused here. Might as well be robust
action_dict['contents'] = action_dict.pop('content')
-1
View File
@@ -1,5 +1,4 @@
from opendevin.agent import Agent
from .agent import PlannerAgent
Agent.register('PlannerAgent', PlannerAgent)
+3 -3
View File
@@ -1,11 +1,11 @@
from typing import List
from .prompt import get_prompt, parse_response
from opendevin.action import Action, AgentFinishAction
from opendevin.agent import Agent
from opendevin.action import AgentFinishAction
from opendevin.llm.llm import LLM
from opendevin.state import State
from .prompt import get_prompt, parse_response
from opendevin.action import Action
class PlannerAgent(Agent):
+46 -36
View File
@@ -1,29 +1,29 @@
import json
from typing import Dict, List, Tuple, Type
from typing import List, Tuple, Dict, Type
from opendevin.plan import Plan
from opendevin.action import Action, action_from_dict
from opendevin.observation import Observation
from opendevin.schema import ActionType
from opendevin.logger import opendevin_logger as logger
from opendevin.action import (
Action,
AddTaskAction,
AgentFinishAction,
AgentRecallAction,
AgentSummarizeAction,
AgentThinkAction,
BrowseURLAction,
CmdKillAction,
NullAction,
CmdRunAction,
CmdKillAction,
BrowseURLAction,
FileReadAction,
FileWriteAction,
AgentRecallAction,
AgentThinkAction,
AgentFinishAction,
AgentSummarizeAction,
AddTaskAction,
ModifyTaskAction,
NullAction,
action_from_dict,
)
from opendevin.logger import opendevin_logger as logger
from opendevin.observation import (
NullObservation,
Observation,
)
from opendevin.plan import Plan
from opendevin.schema import ActionType
ACTION_TYPE_TO_CLASS: Dict[str, Type[Action]] = {
ActionType.RUN: CmdRunAction,
@@ -130,25 +130,6 @@ What is your next thought or action? Again, you must reply with JSON, and only w
"""
def get_hint(latest_action_id: str) -> str:
""" Returns action type hint based on given action_id """
hints = {
'': "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with.",
ActionType.RUN: 'You should think about the command you just ran, what output it gave, and how that affects your plan.',
ActionType.READ: 'You should think about the file you just read, what you learned from it, and how that affects your plan.',
ActionType.WRITE: 'You just changed a file. You should think about how it affects your plan.',
ActionType.BROWSE: 'You should think about the page you just visited, and what you learned from it.',
ActionType.THINK: "Look at your last thought in the history above. What does it suggest? Don't think anymore--take action.",
ActionType.RECALL: 'You should think about the information you just recalled, and how it should affect your plan.',
ActionType.ADD_TASK: 'You should think about the next action to take.',
ActionType.MODIFY_TASK: 'You should think about the next action to take.',
ActionType.SUMMARIZE: '',
ActionType.FINISH: '',
}
return hints.get(latest_action_id, '')
def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
"""
Gets the prompt for the planner agent.
@@ -171,7 +152,7 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
history_dicts.append(action.to_memory())
latest_action = action
if not isinstance(observation, NullObservation):
observation_dict = observation.to_memory()
observation_dict = observation.to_dict()
if (
'extras' in observation_dict
and 'screenshot' in observation_dict['extras']
@@ -179,6 +160,8 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
del observation_dict['extras']['screenshot']
history_dicts.append(observation_dict)
history_str = json.dumps(history_dicts, indent=2)
hint = ''
current_task = plan.get_current_task()
if current_task is not None:
plan_status = f"You're currently working on this task:\n{current_task.goal}."
@@ -186,7 +169,34 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
plan_status += "\nIf it's not achievable AND verifiable with a SINGLE action, you MUST break it down into subtasks NOW."
else:
plan_status = "You're not currently working on any tasks. Your next action MUST be to mark a task as in_progress."
hint = get_hint(latest_action.to_dict()['action'])
hint = plan_status
latest_action_id = latest_action.to_dict()['action']
if current_task is not None:
if latest_action_id == '':
hint = "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with."
elif latest_action_id == ActionType.RUN:
hint = 'You should think about the command you just ran, what output it gave, and how that affects your plan.'
elif latest_action_id == ActionType.READ:
hint = 'You should think about the file you just read, what you learned from it, and how that affects your plan.'
elif latest_action_id == ActionType.WRITE:
hint = 'You just changed a file. You should think about how it affects your plan.'
elif latest_action_id == ActionType.BROWSE:
hint = 'You should think about the page you just visited, and what you learned from it.'
elif latest_action_id == ActionType.THINK:
hint = "Look at your last thought in the history above. What does it suggest? Don't think anymore--take action."
elif latest_action_id == ActionType.RECALL:
hint = 'You should think about the information you just recalled, and how it should affect your plan.'
elif latest_action_id == ActionType.ADD_TASK:
hint = 'You should think about the next action to take.'
elif latest_action_id == ActionType.MODIFY_TASK:
hint = 'You should think about the next action to take.'
elif latest_action_id == ActionType.SUMMARIZE:
hint = ''
elif latest_action_id == ActionType.FINISH:
hint = ''
logger.info('HINT:\n' + hint, extra={'msg_type': 'INFO'})
return prompt % {
'task': plan.main_goal,
-13
View File
@@ -1,13 +0,0 @@
# Docker Containers
Each folder here contains a Dockerfile, and a config.sh describing how to build
the image and where to push it. These are images are built and pushed in GitHub Actions
by the `ghcr.yml` workflow.
## Building Manually
```
docker build -f containers/app/Dockerfile -t opendevin .
docker build -f containers/sandbox/Dockerfile -t sandbox .
docker build -f containers/evaluation/Dockerfile -t evaluation evaluation/SWE-bench/
```
+3 -31
View File
@@ -1,10 +1,8 @@
ARG OPEN_DEVIN_BUILD_VERSION=dev
FROM node:21.7.2-bookworm-slim as frontend-builder
WORKDIR /app
COPY ./frontend/package.json frontend/package-lock.json ./
RUN npm install -g npm@10.5.1
RUN npm install
COPY ./frontend ./
@@ -32,51 +30,25 @@ FROM python:3.12-slim as runtime
WORKDIR /app
ENV RUN_AS_DEVIN=true
ENV SANDBOX_USER_ID=1000
ENV RUN_AS_DEVIN=false
ENV USE_HOST_NETWORK=false
ENV SSH_HOSTNAME=host.docker.internal
ENV WORKSPACE_BASE=/opt/workspace_base
ENV OPEN_DEVIN_BUILD_VERSION=$OPEN_DEVIN_BUILD_VERSION
RUN mkdir -p $WORKSPACE_BASE
RUN apt-get update -y \
&& apt-get install -y curl ssh sudo
RUN useradd -m -u $SANDBOX_USER_ID -s /bin/bash opendevin && \
usermod -aG sudo opendevin && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN chown -R opendevin:opendevin /app
USER opendevin
&& apt-get install -y curl ssh
ENV VIRTUAL_ENV=/app/.venv \
PATH="/app/.venv/bin:$PATH" \
PYTHONPATH='/app'
COPY --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
# change ownership of the virtual environment to the sandbox user
USER root
RUN chown -R opendevin:opendevin ${VIRTUAL_ENV}
USER opendevin
COPY ./opendevin ./opendevin
COPY ./agenthub ./agenthub
RUN python opendevin/download.py # No-op to download assets
RUN playwright install --with-deps chromium
COPY --from=frontend-builder /app/dist ./frontend/dist
USER root
RUN chown -R opendevin:opendevin /app
# make group permissions the same as user permissions
RUN chmod -R g=u /app
USER opendevin
# change ownership of the app directory to the sandbox user
COPY ./containers/app/entrypoint.sh /app/entrypoint.sh
# run the script as root
USER root
RUN chown opendevin:opendevin /app/entrypoint.sh
RUN chmod 777 /app/entrypoint.sh
CMD ["/app/entrypoint.sh"]
CMD ["uvicorn", "opendevin.server.listen:app", "--host", "0.0.0.0", "--port", "3000"]
+1 -3
View File
@@ -1,4 +1,2 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_IMAGE=opendevin
DOCKER_REPOSITORY=ghcr.io/opendevin/opendevin
DOCKER_BASE_DIR="."
-23
View File
@@ -1,23 +0,0 @@
#!/bin/bash
# check user is root
if [ "$(id -u)" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
if [ -z "$SANDBOX_USER_ID" ]; then
echo "SANDBOX_USER_ID is not set"
exit 1
fi
# change uid of opendevin user to match the host user
# but the group id is not changed, so the user can still access everything under /app
usermod -u $SANDBOX_USER_ID opendevin
# get the user group of /var/run/docker.sock and set opendevin to that group
DOCKER_SOCKET_GID=$(stat -c '%g' /var/run/docker.sock)
echo "Docker socket group id: $DOCKER_SOCKET_GID"
usermod -aG $DOCKER_SOCKET_GID opendevin
# switch to the user and start the server
su opendevin -c "cd /app && uvicorn opendevin.server.listen:app --host 0.0.0.0 --port 3000"
+3 -22
View File
@@ -2,20 +2,13 @@
set -eo pipefail
image_name=$1
org_name=$2
push=0
if [[ $3 == "--push" ]]; then
if [[ $2 == "--push" ]]; then
push=1
fi
echo -e "Building: $image_name"
tags=()
OPEN_DEVIN_BUILD_VERSION="dev"
cache_tag_base="buildcache"
cache_tag="$cache_tag_base"
tags=(latest)
if [[ -n $GITHUB_REF_NAME ]]; then
# check if ref name is a version number
if [[ $GITHUB_REF_NAME =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
@@ -24,8 +17,6 @@ if [[ -n $GITHUB_REF_NAME ]]; then
tags+=($major_version $minor_version)
fi
sanitized=$(echo $GITHUB_REF_NAME | sed 's/[^a-zA-Z0-9.-]\+/-/g')
OPEN_DEVIN_BUILD_VERSION=$sanitized
cache_tag+="-${sanitized}"
tags+=($sanitized)
fi
echo "Tags: ${tags[@]}"
@@ -40,28 +31,18 @@ if [ ! -f $dir/config.sh ]; then
exit 1
fi
source $dir/config.sh
if [[ -n "$org_name" ]]; then
DOCKER_ORG="$org_name"
fi
DOCKER_REPOSITORY=$DOCKER_REGISTRY/$DOCKER_ORG/$DOCKER_IMAGE
DOCKER_REPOSITORY=${DOCKER_REPOSITORY,,} # lowercase
echo "Repo: $DOCKER_REPOSITORY"
echo "Base dir: $DOCKER_BASE_DIR"
#docker pull $DOCKER_REPOSITORY:main || true # try to get any cached layers
args=""
for tag in ${tags[@]}; do
args+=" -t $DOCKER_REPOSITORY:$tag"
done
if [[ $push -eq 1 ]]; then
args+=" --push"
args+=" --cache-to=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag,mode=max"
fi
docker buildx build \
$args \
--build-arg OPEN_DEVIN_BUILD_VERSION=$OPEN_DEVIN_BUILD_VERSION \
--cache-from=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag \
--cache-from=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag_base-main \
--platform linux/amd64,linux/arm64 \
--provenance=false \
-f $dir/Dockerfile $DOCKER_BASE_DIR
-19
View File
@@ -1,19 +0,0 @@
FROM ubuntu:22.04
# install basic packages
RUN apt-get update && apt-get install -y \
curl \
wget \
git \
vim \
nano \
unzip \
zip \
python3 \
python3-pip \
python3-venv \
python3-dev \
build-essential \
openssh-server \
sudo \
&& rm -rf /var/lib/apt/lists/*
-15
View File
@@ -1,15 +0,0 @@
# How to build custom E2B sandbox for OpenDevin
[E2B](https://e2b.dev) is an [open-source](https://github.com/e2b-dev/e2b) secure cloud environment (sandbox) made for running AI-generated code and agents. E2B offers [Python](https://pypi.org/project/e2b/) and [JS/TS](https://www.npmjs.com/package/e2b) SDK to spawn and control these sandboxes.
1. Install the CLI with NPM.
```sh
npm install -g @e2b/cli@latest
```
Full CLI API is [here](https://e2b.dev/docs/cli/installation).
1. Build the sandbox
```sh
e2b template build --dockerfile ./Dockerfile --name "open-devin"
```
-14
View File
@@ -1,14 +0,0 @@
# This is a config for E2B sandbox template.
# You can use 'template_id' (785n69crgahmz0lkdw9h) or 'template_name (open-devin) from this config to spawn a sandbox:
# Python SDK
# from e2b import Sandbox
# sandbox = Sandbox(template='open-devin')
# JS SDK
# import { Sandbox } from 'e2b'
# const sandbox = await Sandbox.create({ template: 'open-devin' })
dockerfile = "Dockerfile"
template_name = "open-devin"
template_id = "785n69crgahmz0lkdw9h"
+1 -3
View File
@@ -1,4 +1,2 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_IMAGE=eval-swe-bench
DOCKER_REPOSITORY=ghcr.io/opendevin/eval-swe-bench
DOCKER_BASE_DIR=evaluation/SWE-bench
+1 -13
View File
@@ -16,18 +16,6 @@ RUN apt-get update && apt-get install -y \
build-essential \
openssh-server \
sudo \
bash \
gcc \
jq \
g++ \
make \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p -m0755 /var/run/sshd
# symlink python3 to python
RUN ln -s /usr/bin/python3 /usr/bin/python
# install basic dependencies for CodeActAgent
RUN pip3 install --upgrade pip
RUN pip3 install jupyterlab notebook jupyter_kernel_gateway flake8
RUN service ssh start
+1 -3
View File
@@ -1,4 +1,2 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_IMAGE=sandbox
DOCKER_REPOSITORY=ghcr.io/opendevin/sandbox
DOCKER_BASE_DIR="."
+21 -22
View File
@@ -3,20 +3,29 @@ repos:
rev: v4.5.0
hooks:
- id: trailing-whitespace
exclude: docs/modules/python
- id: end-of-file-fixer
exclude: docs/modules/python
- id: check-yaml
- id: debug-statements
- repo: https://github.com/tox-dev/pyproject-fmt
rev: 1.7.0
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.16
- id: flake8
args: ['--select=Q000'] # Q000 is the error code for single quote enforcement
additional_dependencies:
- flake8-quotes
- repo: https://github.com/hhatto/autopep8
rev: v2.1.0
hooks:
- id: validate-pyproject
- id: autopep8
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.5.0
hooks:
- id: setup-cfg-fmt
always_run: true
pass_filenames: false
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
@@ -25,28 +34,18 @@ repos:
# Run the linter.
- id: ruff
entry: ruff check --config dev_config/python/ruff.toml opendevin/ agenthub/
types_or: [python, pyi, jupyter]
args: [--fix]
types_or: [ python, pyi, jupyter ]
args: [ --fix ]
# Run the formatter.
- id: ruff-format
entry: ruff check --config dev_config/python/ruff.toml opendevin/ agenthub/
types_or: [python, pyi, jupyter]
types_or: [ python, pyi, jupyter ]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
additional_dependencies:
[types-requests, types-setuptools, types-pyyaml, types-toml]
additional_dependencies: [types-requests, types-setuptools, types-pyyaml, types-toml]
entry: mypy --config-file dev_config/python/mypy.ini opendevin/ agenthub/
always_run: true
pass_filenames: false
- repo: local
hooks:
- id: pydoc
name: pydoc
entry: ./dev_config/python/pydoc.sh
language: script
types: [python]
pass_filenames: false
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
pip install pydoc-markdown && rm -rf ./docs/modules/python && pydoc-markdown
+1 -19
View File
@@ -1,21 +1,3 @@
exclude = [
"agenthub/monologue_agent/regression/",
]
[lint]
select = [
"E",
"W",
"F",
"I",
"Q",
]
ignore = [
"E501",
]
flake8-quotes = {inline-quotes = "single"}
[format]
quote-style = "single"
]
-20
View File
@@ -1,20 +0,0 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+251
View File
@@ -0,0 +1,251 @@
> 警告:此说明文件可能已过时。应将 README.md 视为真实的来源。如果您注意到差异,请打开一个拉取请求以更新此说明文件。
[English](../README.md) | [中文](README-zh.md)
<a name="readme-top"></a>
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
*** or simply open an issue with the tag "enhancement".
*** Don't forget to give the project a star!
*** Thanks again! Now go create something AMAZING! :D
-->
<!-- PROJECT SHIELDS -->
<!--
*** I'm using markdown "reference style" links for readability.
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
*** See the bottom of this document for the declaration of the reference variables
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
<div align="center">
<a href="https://github.com/OpenDevin/OpenDevin/graphs/contributors"><img src="https://img.shields.io/github/contributors/opendevin/opendevin?style=for-the-badge" alt="Contributors"></a>
<a href="https://github.com/OpenDevin/OpenDevin/network/members"><img src="https://img.shields.io/github/forks/opendevin/opendevin?style=for-the-badge" alt="Forks"></a>
<a href="https://github.com/OpenDevin/OpenDevin/stargazers"><img src="https://img.shields.io/github/stars/opendevin/opendevin?style=for-the-badge" alt="Stargazers"></a>
<a href="https://github.com/OpenDevin/OpenDevin/issues"><img src="https://img.shields.io/github/issues/opendevin/opendevin?style=for-the-badge" alt="Issues"></a>
<a href="https://github.com/OpenDevin/OpenDevin/blob/main/LICENSE"><img src="https://img.shields.io/github/license/opendevin/opendevin?style=for-the-badge" alt="MIT License"></a>
</br>
<a href="https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="Join our Slack community"></a>
<a href="https://discord.gg/mBuDGRzzES"><img src="https://img.shields.io/badge/Discord-Join%20Us-purple?logo=discord&logoColor=white&style=for-the-badge" alt="Join our Discord community"></a>
</div>
<!-- PROJECT LOGO -->
<div align="center">
<img src="../logo.png" alt="Logo" width="200" height="200">
<h1 align="center">OpenDevin:少写代码,多创作</h1>
</div>
<!-- TABLE OF CONTENTS -->
<details>
<summary>🗂️ Table of Contents</summary>
<ol>
<li><a href="#-mission">🎯 Mission</a></li>
<li><a href="#-what-is-devin">🤔 What is Devin?</a></li>
<li><a href="#-why-opendevin">🐚 Why OpenDevin?</a></li>
<li><a href="#-project-status">🚧 Project Status</a></li>
<a href="#-get-started">🚀 Get Started</a>
<ul>
<li><a href="#1-requirements">1. Requirements</a></li>
<li><a href="#2-build-and-setup">2. Build and Setup</a></li>
<li><a href="#3-run-the-application">3. Run the Application</a></li>
<li><a href="#4-individual-server-startup">4. Individual Server Startup</a></li>
<li><a href="#5-help">5. Help</a></li>
</ul>
</li>
<li><a href="#%EF%B8%8F-research-strategy">⭐️ Research Strategy</a></li>
<li><a href="#-how-to-contribute">🤝 How to Contribute</a></li>
<li><a href="#-join-our-community">🤖 Join Our Community</a></li>
<li><a href="#%EF%B8%8F-built-with">🛠️ Built With</a></li>
<li><a href="#-license">📜 License</a></li>
</ol>
</details>
## 🎯 使命
[Project Demo Video](https://github.com/OpenDevin/OpenDevin/assets/38853559/71a472cc-df34-430c-8b1d-4d7286c807c9)
欢迎来到 OpenDevin,一个开源项目,旨在复制 Devin,一款自主的 AI 软件工程师,能够执行复杂的工程任务,并与用户积极合作,共同进行软件开发项目。该项目立志通过开源社区的力量复制、增强和创新 Devin。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🤔 Devin 是什么?
Devin 代表着一种尖端的自主代理程序,旨在应对软件工程的复杂性。它利用诸如 shell、代码编辑器和 Web 浏览器等工具的组合,展示了在软件开发中利用 LLMs(大型语言模型)的未开发潜力。我们的目标是探索和拓展 Devin 的能力,找出其优势和改进空间,以指导开源代码模型的进展。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🐚 为什么选择 OpenDevin
OpenDevin 项目源于对复制、增强和超越原始 Devin 模型的愿望。通过与开源社区的互动,我们旨在解决 Code LLMs 在实际场景中面临的挑战,创作出对社区有重大贡献并为未来进步铺平道路的作品。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🚧 项目状态
OpenDevin 目前仍在进行中,但您已经可以运行 alpha 版本来查看端到端系统的运行情况。项目团队正在积极努力实现以下关键里程碑:
- **用户界面(UI)**:开发用户友好的界面,包括聊天界面、演示命令的 shell 和 Web 浏览器。
- **架构**:构建一个稳定的代理框架,具有强大的后端,可以读取、写入和运行简单的命令。
- **代理能力**:增强代理的能力,以生成 bash 脚本、运行测试和执行其他软件工程任务。
- **评估**:建立一个与 Devin 评估标准一致的最小评估流水线。
在完成 MVP 后,团队将专注于各个领域的研究,包括基础模型、专家能力、评估和代理研究。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## ⚠️ 注意事项和警告
* OpenDevin 仍然是一个 alpha 项目。它变化很快且不稳定。我们正在努力在未来几周发布稳定版本。
* OpenDevin 会向您配置的 LLM 发出许多提示。大多数 LLM 都需要花费金钱,请务必设置花费限制并监控使用情况。
* OpenDevin 在 Docker 沙箱中运行 `bash` 命令,因此不应影响您的计算机。但您的工作区目录将附加到该沙箱,并且目录中的文件可能会被修改或删除。
* 我们默认的代理目前是 MonologueAgent,具有有限的功能,但相当稳定。我们正在开发其他代理实现,包括 [SWE 代理](https://swe-agent.com/)。您可以[在这里阅读我们当前的代理集合](./docs/documentation/Agents.md)。
## 🚀 开始
开始使用 OpenDevin 项目非常简单。按照以下简单步骤在您的系统上设置和运行 OpenDevin:
运行 OpenDevin 最简单的方法是在 Docker 容器中。
您可以运行:
```bash
# 您的 OpenAI API 密钥,或任何其他 LLM API 密钥
export LLM_API_KEY="sk-..."
# 您想要 OpenDevin 修改的目录。必须是绝对路径!
export WORKSPACE_DIR=$(pwd)/workspace
docker run \
-e LLM_API_KEY \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_DIR \
-v $WORKSPACE_DIR:/opt/workspace_base \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 3000:3000 \
ghcr.io/opendevin/opendevin:latest
```
`$(pwd)/workspace` 替换为您希望 OpenDevin 使用的代码路径。
您可以在 `http://localhost:3000` 找到正在运行的 OpenDevin。
请参阅[Development.md](Development.md)以获取在没有 Docker 的情况下运行 OpenDevin 的说明。
## 🤖 LLM 后端
OpenDevin 可以与任何 LLM 后端配合使用。
要获取提供的 LM 提供商和模型的完整列表,请参阅
[litellm 文档](https://docs.litellm.ai/docs/providers)。
`LLM_MODEL` 环境变量控制在编程交互中使用哪个模型,
但在 OpenDevin UI 中选择模型将覆盖此设置。
对于某些 LLM,可能需要以下环境变量:
* `LLM_API_KEY`
* `LLM_BASE_URL`
* `LLM_EMBEDDING_MODEL`
* `LLM_DEPLOYMENT_NAME`
* `LLM_API_VERSION`
**关于替代模型的说明:**
某些替代模型可能比其他模型更具挑战性。
不要害怕,勇敢的冒险家!我们将很快公布 LLM 特定的文档,指导您完成您的探险。
如果您已经掌握了除 OpenAI 的 GPT 之外的模型使用技巧,
我们鼓励您[与我们分享您的设置说明](https://github.com/OpenDevin/OpenDevin/issues/417)。
还有[使用 ollama 运行本地模型的文档](./docs/documentation/LOCAL_LLM_GUIDE.md)。
## ⭐️ 研究策略
利用 LLMs 实现生产级应用程序的完全复制是一个复杂的任务。我们的策略包括:
1. **核心技术研究:** 专注于基础研究,以了解和改进代码生成和处理的技术方面。
2. **专业能力:** 通过数据整理、训练方法等手段增强核心组件的效能。
3. **任务规划:** 开发能力,用于错误检测、代码库管理和优化。
4. **评估:** 建立全面的评估指标,以更好地了解和改进我们的模型。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🤝 如何贡献
OpenDevin 是一个社区驱动的项目,我们欢迎所有人的贡献。无论您是开发人员、研究人员,还是对利用人工智能推动软件工程领域发展充满热情的人,都有许多参与方式:
- **代码贡献:** 帮助我们开发核心功能、前端界面或沙盒解决方案。
- **研究和评估:** 为我们对软件工程中的 LLMs 的理解做出贡献,参与评估模型,或提出改进意见。
- **反馈和测试:** 使用 OpenDevin 工具集,报告错误,提出功能建议,或就可用性提供反馈。
详情请查看[此文档](./CONTRIBUTING.md)。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🤖 加入我们的社区
现在我们既有 Slack 工作空间用于协作构建 OpenDevin,也有 Discord 服务器用于讨论与项目、LLM、Agent 等相关的任何事情。
* [Slack 工作空间](https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw)
* [Discord 服务器](https://discord.gg/mBuDGRzzES)
如果你愿意贡献,欢迎加入我们的社区(请注意,现在无需填写[表格](https://forms.gle/758d5p6Ve8r2nxxq6))。让我们一起简化软件工程!
🐚 **少写代码,用OpenDevin创造更多。**
[![Star History Chart](https://api.star-history.com/svg?repos=OpenDevin/OpenDevin&type=Date)](https://star-history.com/#OpenDevin/OpenDevin&Date)
## 🛠️ 技术栈
OpenDevin 使用了一系列强大的框架和库的组合,为其开发提供了坚实的基础。以下是项目中使用的关键技术:
![FastAPI](https://img.shields.io/badge/FastAPI-black?style=for-the-badge) ![uvicorn](https://img.shields.io/badge/uvicorn-black?style=for-the-badge) ![LiteLLM](https://img.shields.io/badge/LiteLLM-black?style=for-the-badge) ![Docker](https://img.shields.io/badge/Docker-black?style=for-the-badge) ![Ruff](https://img.shields.io/badge/Ruff-black?style=for-the-badge) ![MyPy](https://img.shields.io/badge/MyPy-black?style=for-the-badge) ![LlamaIndex](https://img.shields.io/badge/LlamaIndex-black?style=for-the-badge) ![React](https://img.shields.io/badge/React-black?style=for-the-badge)
请注意,这些技术的选择正在进行中,随着项目的发展,可能会添加其他技术或移除现有技术。我们致力于采用最合适和最有效的工具,以增强 OpenDevin 的功能。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 📜 许可证
根据 MIT 许可证分发。有关更多信息,请参阅 [`LICENSE`](./LICENSE)。
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
[contributors-shield]: https://img.shields.io/github/contributors/opendevin/opendevin?style=for-the-badge
[contributors-url]: https://github.com/OpenDevin/OpenDevin/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/opendevin/opendevin?style=for-the-badge
[forks-url]: https://github.com/OpenDevin/OpenDevin/network/members
[stars-shield]: https://img.shields.io/github/stars/opendevin/opendevin?style=for-the-badge
[stars-url]: https://github.com/OpenDevin/OpenDevin/stargazers
[issues-shield]: https://img.shields.io/github/issues/opendevin/opendevin?style=for-the-badge
[issues-url]: https://github.com/OpenDevin/OpenDevin/issues
[license-shield]: https://img.shields.io/github/license/opendevin/opendevin?style=for-the-badge
[license-url]: https://github.com/OpenDevin/OpenDevin/blob/main/LICENSE
-41
View File
@@ -1,41 +0,0 @@
# Website
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
### Installation
```
$ yarn
```
### Local Development
```
$ yarn start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build
```
$ yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
Using SSH:
```
$ USE_SSH=true yarn deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
+14
View File
@@ -0,0 +1,14 @@
# System Architecture Overview
This is a high-level overview of the system architecture. The system is divided into two main components: the frontend and the backend. The frontend is responsible for handling user interactions and displaying the results. The backend is responsible for handling the business logic and executing the agents.
![system_architecture.svg](system_architecture.svg)
This Overview is simplified to show the main components and their interactions. For a more detailed view of the backend architecture, see the [Backend Architecture](#backend-architecture) section.
# Backend Architecture
*__Disclaimer__: The backend architecture is a work in progress and is subject to change. The following diagram shows the current architecture of the backend based on the commit that is shown in the footer of the diagram.*
![backend_architecture.svg](backend_architecture.svg)
+23
View File
@@ -0,0 +1,23 @@
# Process for updating the backend architecture diagram
The generation of the backend architecture diagram is partially automated. The diagram is generated from the type hints in the code using the py2puml tool. The diagram is then manually reviewed, adjusted and exported to PNG and SVG.
## Prerequisites
- Running python environment in which opendevin is executable (according to the instructions in the README.md file in the root of the repository)
- [py2puml](https://github.com/lucsorel/py2puml) installed
## Steps
1. Autogenerate the diagram by running the following command from the root of the repository:
```py2puml opendevin opendevin > docs/architecture/backend_architecture.puml```
2. Open the generated file in a PlantUML editor, e.g. Visual Studio Code with the PlantUML extension or [PlantText](https://www.planttext.com/)
3. Review the generated PUML and make all necessary adjustments to the diagram (add missing parts, fix mistakes, improve positioning).
*py2puml creates the diagram based on the type hints in the code, so missing or incorrect type hints may result in an incomplete or incorrect diagram.*
4. Review the diff between the new and the previous diagram and manually check if the changes are correct.
*Make sure not to remove parts that were manually added to the diagram in the past and are still relevant.*
4. Add the commit hash of the commit that was used to generate the diagram to the diagram footer.
5. Export the diagram as PNG and SVG files and replace the existing diagrams in the `docs/architecture` directory. This can be done with (e.g. [PlantText](https://www.planttext.com/))

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 267 KiB

@@ -219,4 +219,4 @@ opendevin.controller.agent_controller.AgentController -> opendevin.controller.co
opendevin.controller.command_manager.CommandManager -> opendevin.sandbox.sandbox.DockerInteractive
footer Based on f3fda42; Generated by //py2puml//
@enduml
@enduml
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

@@ -2,13 +2,13 @@
node frontend as frontend{
component App
package components{
component Terminal
component ChatInterface
component BannerSettings
@@ -37,8 +37,8 @@ node frontend as frontend{
Terminal -[hidden]u-> ChatInterface
ChatInterface -[hidden]u-> BannerSettings
interface "HTTP (:3001)" as HTTP
HTTP - App
@@ -50,13 +50,13 @@ node backend{
component Server
'defined in server/server.py, port is defined at startup with uvicorn
interface "Client WS\n(:3000/ws)" as client_socket
interface "Client WS\n(:3000/ws)" as client_socket
client_socket - Server
}
node AgentController{
node AgentController{
}
Server -d-> AgentController
}
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

-3
View File
@@ -1,3 +0,0 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
@@ -1,37 +1,31 @@
# Azure OpenAI LLM
# Azure OpenAI LLM Guide
## Completion
# 1. Completion
OpenDevin uses LiteLLM for completion calls. You can find their documentation on Azure [here](https://docs.litellm.ai/docs/providers/azure)
### Azure openai configs
## azure openai configs
When running the OpenDevin Docker image, you'll need to set the following environment variables using `-e`:
```
LLM_BASE_URL="<azure-api-base-url>" # e.g. "https://openai-gpt-4-test-v-1.openai.azure.com/"
LLM_API_KEY="<azure-api-key>"
LLM_MODEL="azure/<your-gpt-deployment-name>"
LLM_API_VERSION = "<api-version>" # e.g. "2024-02-15-preview"
AZURE_API_VERSION = "<api-version>" # e.g. "2024-02-15-preview"
```
:::note
You can find your ChatGPT deployment name on the deployments page in Azure. It could be the same with the chat model name (e.g. 'GPT4-1106-preview'), by default or initially set, but it doesn't have to be the same. Run opendevin, and when you load it in the browser, go to Settings and set model as above: "azure/&lt;your-actual-gpt-deployment-name&gt;". If it's not in the list, enter your own text and save it.
:::
## Embeddings
# 2. Embeddings
OpenDevin uses llama-index for embeddings. You can find their documentation on Azure [here](https://docs.llamaindex.ai/en/stable/api_reference/embeddings/azure_openai/)
### Azure openai configs
## azure openai configs
The model used for Azure OpenAI embeddings is "text-embedding-ada-002".
You need the correct deployment name for this model in your Azure account.
When running OpenDevin in Docker, set the following environment variables using `-e`:
```
LLM_EMBEDDING_MODEL="azureopenai"
LLM_EMBEDDING_DEPLOYMENT_NAME = "<your-embedding-deployment-name>" # e.g. "TextEmbedding...<etc>"
DEPLOYMENT_NAME = "<your-embedding-deployment-name>" # e.g. "TextEmbedding...<etc>"
LLM_API_VERSION = "<api-version>" # e.g. "2024-02-15-preview"
```
+96
View File
@@ -0,0 +1,96 @@
# Agents and Capabilities
## Monologue Agent:
### Description:
The Monologue Agent utilizes long and short term memory to complete tasks.
Long term memory is stored as a LongTermMemory object and the model uses it to search for examples from the past.
Short term memory is stored as a Monologue object and the model can condense it as necessary.
### Actions:
`Action`,
`NullAction`,
`CmdRunAction`,
`FileWriteAction`,
`FileReadAction`,
`AgentRecallAction`,
`BrowseURLAction`,
`AgentThinkAction`
### Observations:
`Observation`,
`NullObservation`,
`CmdOutputObservation`,
`FileReadObservation`,
`AgentRecallObservation`,
`BrowserOutputObservation`
### Methods:
`__init__`: Initializes the agent with a long term memory, and an internal monologue
`_add_event`: Appends events to the monologue of the agent and condenses with summary automatically if the monologue is too long
`_initialize`: Utilizes the `INITIAL_THOUGHTS` list to give the agent a context for its capabilities and how to navigate the `/workspace`
`step`: Modifies the current state by adding the most rescent actions and observations, then prompts the model to think about its next action to take.
`search_memory`: Uses `VectorIndexRetriever` to find related memories within the long term memory.
## Planner Agent:
### Description:
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
### Actions:
`NullAction`,
`CmdRunAction`,
`CmdKillAction`,
`BrowseURLAction`,
`FileReadAction`,
`FileWriteAction`,
`AgentRecallAction`,
`AgentThinkAction`,
`AgentFinishAction`,
`AgentSummarizeAction`,
`AddTaskAction`,
`ModifyTaskAction`,
### Observations:
`Observation`,
`NullObservation`,
`CmdOutputObservation`,
`FileReadObservation`,
`AgentRecallObservation`,
`BrowserOutputObservation`
### Methods:
`__init__`: Initializes an agent with `llm`
`step`: Checks to see if current step is completed, returns `AgentFinishAction` if True. Otherwise, creates a plan prompt and sends to model for inference, adding the result as the next action.
`search_memory`: Not yet implemented
## CodeAct Agent:
### Description:
The Code Act Agent is a minimalist agent. The agent works by passing the model a list of action-observation pairs and prompting the model to take the next step.
### Actions:
`Action`,
`CmdRunAction`,
`AgentEchoAction`,
`AgentFinishAction`,
### Observations:
`CmdOutputObservation`,
`AgentMessageObservation`,
### Methods:
`__init__`: Initializes an agent with `llm` and a list of messages `List[Mapping[str, str]]`
`step`: First, gets messages from state and then compiles them into a list for context. Next, pass the context list with the prompt to get the next command to execute. Finally, Execute command if valid, else return `AgentEchoAction(INVALID_INPUT_MESSAGE)`
`search_memory`: Not yet implemented
+108
View File
@@ -0,0 +1,108 @@
# Local LLM Guide with Ollama server
## 0. Install Ollama:
run the following command in a conda env with CUDA etc.
Linux:
```
curl -fsSL https://ollama.com/install.sh | sh
```
Windows or macOS:
- Download from [here](https://ollama.com/download/)
## 1. Install Models:
Ollama model names can be found [here](https://ollama.com/library) (See example below)
![alt text](images/ollama.png)
Once you have found the model you want to use copy the command and run it in your conda env.
Example of llama2 q4 quantized:
```
conda activate <env_name>
ollama run llama2:13b-chat-q4_K_M
```
you can check which models you have downloaded like this:
```
~$ ollama list
NAME ID SIZE MODIFIED
llama2:latest 78e26419b446 3.8 GB 6 weeks ago
mistral:7b-instruct-v0.2-q4_K_M eb14864c7427 4.4 GB 2 weeks ago
starcoder2:latest f67ae0f64584 1.7 GB 19 hours ago
```
## 2. Run Ollama in CLI:
This command starts up the ollama server that is on port `11434`
This will show the requests in CLI
```
conda activate <env_name>
ollama serve
```
or
This will run with no output in the background
```
sudo systemctl start ollama
```
If you see something like this:
```
Error: listen tcp 127.0.0.1:11434: bind: address already in use
```
This is not an error it just means the server is already running
To stop the server use:
```
sudo systemctl stop ollama
```
For more info go [here](https://github.com/ollama/ollama/blob/main/docs/faq.md)
## 3. Start OpenDevin
Use the instructions in [README.md](/README.md) to start OpenDevin using Docker.
When running `docker run`, add the following environment variables using `-e`:
```
LLM_API_KEY="ollama"
LLM_MODEL="ollama/<model_name>"
LLM_EMBEDDING_MODEL="local"
LLM_BASE_URL="http://localhost:<port_number>"
WORKSPACE_DIR="./workspace"
```
Notes:
- The API key should be set to `"ollama"`
- The base url needs to be `localhost`
- By default ollama port is `11434` unless you set it
- `model_name` needs to be the entire model name
- Example: `LLM_MODEL="ollama/llama2:13b-chat-q4_K_M"`
You should now be able to connect to `http://localhost:3001/` with your local model running!
## Additional Notes for WSL2 Users:
1. If you encounter the following error during setup: `Exception: Failed to create opendevin user in sandbox: b'useradd: UID 0 is not unique\n'`
You can resolve it by running:
```
export SANDBOX_USER_ID=1000
```
2. If you face issues running Poetry even after installing it during the build process, you may need to add its binary path to your environment:
```
export PATH="$HOME/.local/bin:$PATH"
```
3. If you experiencing issues related to networking, such as `NoneType object has no attribute 'request'` when executing `make run`, you may need to configure your WSL2 networking settings. Follow these steps:
- Open or create the `.wslconfig` file located at `C:\Users\%username%\.wslconfig` on your Windows host machine.
- Add the following configuration to the `.wslconfig` file:
```
[wsl2]
networkingMode=mirrored
localhostForwarding=true
```
- Save the `.wslconfig` file.
- Restart WSL2 completely by exiting any running WSL2 instances and executing the command `wsl --shutdown` in your command prompt or terminal.
- After restarting WSL, attempt to execute `make run` again. The networking issue should be resolved.
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

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