Compare commits

..

57 Commits

Author SHA1 Message Date
Robert Brennan 2be7e55303 Prompt for settings on initial load, and add migration logic (#1527)
* prompt for settings on initial load and add migration logic

* logspam

* revert message

* change fn body

* fix fn

* move agent box to top

* add message about update

* fix up settings logic

* pr feedback

* fix up lint

* Update frontend/src/components/modals/settings/SettingsModal.tsx

Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>

* Update frontend/src/components/modals/settings/SettingsModal.tsx

Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>

* disable save if required settings arent set

* simplify required settings

* fix up vars

* lint

* fix compile issues

* fix test

* fix test

* fix all tests

* lint

* fix build err

* lint

* more lint

---------

Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2024-05-06 18:30:18 +00:00
Frank Xu 26dcf4fd7c remove screenshot in browser observation (#1588)
* remove screenshot in browser observation

* refactor utils

* allow only dict

* fix screenshot not showing up in frontend

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-05-06 13:56:28 -04:00
Robert Brennan d0967122f8 Minor changes to agent state management (#1597)
* move towards event stream

* refactor agent state changes

* move agent state logic

* fix callbacks

* break on finish

* closer to working

* change frontend to accomodate new flow

* handle start action

* fix locked stream

* revert message

* logspam

* no async on close

* get rid of agent_task

* fix up closing

* better asyncio handling

* sleep to give back control

* fix key

* logspam

* update frontend agent state actions

* fix pause and cancel

* delint

* fix map

* delint

* wait for agent to finish

* fix unit test

* event stream enums

* fix merge issues

* fix lint

* fix test

* fix test

* add user message action

* add user message action

* fix up user messages

* fix main.py flow

* refactor message waiting

* lint

* fix test

* fix test

* simplify if/else

* fix state reset

* logspam

* add error status

* minor changes to control bar

* handle user messages when not awaiting

* restart agent after stopping

* Update opendevin/controller/agent_controller.py

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>

* delint

* refactor initialize

* delint

* fix dispatch

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-05-06 12:29:48 +00:00
Xia Zhenhua c9970e6817 fix: delegateAgent select micro agents name bug. (#1601)
* fix: delegateAgent select micro agents name bug.

* refactor: last_observation

---------

Co-authored-by: aaren.xzh <aaren.xzh@antfin.com>
2024-05-06 08:27:03 -04:00
ChengyangDu 16400e40ca fix: check (and create if necessary) dir before write file (#1600) 2024-05-06 08:18:32 -04:00
Xia Zhenhua 2d24521222 fix: file in _instructions directory not set correctly. (#1602)
Co-authored-by: aaren.xzh <aaren.xzh@antfin.com>
2024-05-06 08:09:03 -04:00
Leo 886f713e4c Fix: Compatible with older docker version for --add-host (#1604)
Signed-off-by: ifuryst <ifuryst@gmail.com>
2024-05-06 07:59:51 -04:00
ChengyangDu a58f41a7b3 fix: check action['action'] instance type before parsing action cls (#1599) 2024-05-06 08:41:14 +02:00
Alex Bäuerle ae2b857cb4 docs(docker): update docker version and make get started more prominent (#1536)
* docs(docker): update docker version and make get started more prominent

* bump version
2024-05-06 00:40:27 +00:00
sp.wack 35b248ffd6 update/refactor unit tests (#1598) 2024-05-05 20:27:21 -04:00
Shimada666 3dbd502510 feat: customize the tab list displayed for different agents (#1543) 2024-05-05 20:18:05 -04:00
Christian Balcom 27e13fafb5 Token counting and litellm provider customization (#1421)
* Count tokens to judge more accurate max monologue length, add configurations for max input and output tokens, pulling from litellm when available.

* Fix token counter

* Use None as the default for llm_custom_llm_provider, resolve settings conflict with recent command-r-plus commit.

* Document rationale for default token counts.

* Update opendevin/llm/llm.py

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>

* Update opendevin/llm/llm.py

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>

* Reverting formatting changes from merge.

* Maybe this will satisfy pydoc-markdown?

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-05-06 00:43:00 +02:00
Robert Brennan f7e0c6cd06 Separate agent controller and server via EventStream (#1538)
* move towards event stream

* refactor agent state changes

* move agent state logic

* fix callbacks

* break on finish

* closer to working

* change frontend to accomodate new flow

* handle start action

* fix locked stream

* revert message

* logspam

* no async on close

* get rid of agent_task

* fix up closing

* better asyncio handling

* sleep to give back control

* fix key

* logspam

* update frontend agent state actions

* fix pause and cancel

* delint

* fix map

* delint

* wait for agent to finish

* fix unit test

* event stream enums

* fix merge issues

* fix lint

* fix test

* fix test

* add user message action

* add user message action

* fix up user messages

* fix main.py flow

* refactor message waiting

* lint

* fix test

* fix test
2024-05-05 19:20:01 +00:00
Aleksandar 4e84aac577 Add to Local LLM README.md -e SANDBOX_USER_ID=501 (#1592) 2024-05-05 08:16:20 -04:00
Shimada666 224ddf3ac0 feat: support jupyter auto scroll to bottom (#1558)
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2024-05-05 11:46:34 +03:00
Robert Brennan 7b6b4e3a11 be more dynamic around uid generation (#1584)
* be more dynamic around uid generation

* fix comment

* fix second uid add
2024-05-04 22:26:55 -04:00
Zhou Hang 24c5bcb502 i18n: Fix and add zh-CN 简体中文 (#1577)
* i18n: fix&add zh-CN translation

* i18n: Improve translation
2024-05-04 21:26:30 -04:00
Paulo Ferreira 4292a4aa32 Fix README.md formatting: Removed unnecessary backticks (#1581) 2024-05-04 20:06:15 +00:00
Shimada666 30de484d49 feat: merge identical url from different error messages (#1572) 2024-05-04 15:55:10 -04:00
sp.wack 718730a2b9 (refactor|test)(frontend): chat (#1488)
* initial commit

* update tests and feat-markdown

* rename

* introduce chat

* initial commit

* extend chatinterface, add
new store, and utilize new reducers

* improve styles, code markdown, and adjust styles

* update actions to use new reducers

* scroll down for new messages

* add fade and action banner

* support with jupyter

* refactor to pass tests

* remove unused import

* remove outdated files/folders

* create simple useTyping hook

* remove action banner, extend tests, move files

* extend tests

* disable error
2024-05-04 12:11:47 -07:00
RainRat 4eeaac28eb Update mixin.py (#1579)
fix typo
2024-05-04 20:17:49 +02:00
快乐的老鼠宝宝 8961ceabc6 docs: translation of zh-TW (#1580) 2024-05-05 00:24:28 +08:00
Robert Brennan 730fcd7a39 change default to CodeActAgent and update instructions (#1523)
* change default to CodeActAgent and update instructions

* add quickstart

* change code

* unpin patch version

* Apply suggestions from code review
2024-05-04 08:37:23 -04:00
Robert Brennan 72efa05c71 Update ghcr.yml (#1576) 2024-05-04 08:32:12 -04:00
Robert Brennan 322b23550d run ghcr on tag (#1575) 2024-05-04 12:18:21 +00:00
Robert Brennan 1d92e9a27b Fix user permissions in docker (#1565)
* permissions mostly working

* fix browser
2024-05-04 13:08:43 +08:00
Leo 6013faeec5 Add frontend tests to pre-commit and Makefile. (#1549)
Signed-off-by: ifuryst <ifuryst@gmail.com>
2024-05-03 16:15:22 -04:00
Jiayi Pan bccb8297b8 feat: ability to configure temperature and top-p sampling for llm generation (#1556)
Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-05-03 19:15:39 +00:00
Binyuan Hui b2a99b509f fix the issue where session id might be duplicated (#1557)
Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
2024-05-03 15:47:19 +00:00
Robert Brennan 7d8e10aa18 Remove v prefix on tags (#1555) 2024-05-03 10:36:09 -04:00
Graham Neubig 74e159add6 Remove screenshot from microagent prompt (#1550)
* Remove screenshot from microagent prompt

* Update recursive search

* Update to handle various data types
2024-05-03 09:34:39 -04:00
Anas DORBANI 91c8f1cb3f Update README.md (#1548)
Add documentation badge
2024-05-03 20:09:17 +08:00
Junyang Lin 7d2d2ab242 Update README.md
update slack invite link
2024-05-03 15:57:34 +08:00
RainRat 3cdff79173 fix typos (#1537) 2024-05-03 09:41:32 +03:00
Xingyao Wang 3886c51217 Update CodeAct README.md (#1534)
* Update README.md

* update documentation in docstring
2024-05-03 02:32:53 +00:00
Robert Brennan 2c3fd1322d default to codeact agent (#1535) 2024-05-02 21:55:30 -04:00
Robert Brennan 99e4dd1730 Update social links (#1539)
* Update social links

* fix more slack links

* update discord

* update reach out faq

* fix init in test

* revert test
2024-05-02 23:08:39 +00:00
Alex Bäuerle d9ba45dae8 ci(docs): only generate autogen python docs on deploy (#1501)
* ci(docs): only generate autogen python docs on deploy

* poetry

* workdir
2024-05-02 18:29:41 +00:00
Shimada666 04306b0219 feat: adjust scroll timeout and scroll behavior (#1526)
Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
2024-05-02 14:20:16 -04:00
Robert Brennan 7914258945 Fix docker build, integration test (#1533)
* fix docker build

* revert plugins

* fix imports

* fix import

* fix one more import
2024-05-02 13:49:25 -04:00
Robert Brennan fadcdc117e Migrate to new folder structure in preparation for refactor (#1531)
* fix up folder structure

* update docs

* fix imports

* fix imports

* fix imoprt

* fix imports

* fix imports

* fix imports

* fix test import

* fix tests

* fix main import
2024-05-02 17:01:54 +00:00
Robert Brennan ce7c7eaae4 Refactor actions and observations (#1479)
* refactor actions and events

* remove type_key

* remove stream

* move import

* move import

* fix NullObs

* reorder imports

* fix lint

* fix dataclasses

* remove blank fields

* fix nullobs

* fix sidebar labels

* fix test compilation

* switch to asdict

* lint

* fix whitespace

* fix executable

* delint

* fix run

* remove NotImplementeds

* fix path prefix

* remove null files

* add debug

* add more debug info

* fix dataclass on null

* remove debug

* revert sandbox

* fix merge issues

* fix tyeps

* Update opendevin/events/action/browse.py
2024-05-02 15:44:54 +00:00
Xingyao Wang f19333aa6e fix(controller): "Current task state not recognized" (#1517)
* fix agent task already running bug

* attemp to fix "Current task state not recognized"

* Revert "attemp to fix "Current task state not recognized""

This reverts commit f5cbfe1ebb.

* attemp to fix "Current task state not recognized"

* fix invalid state for reset

---------

Co-authored-by: Leo <ifuryst@gmail.com>
2024-05-02 23:34:55 +08:00
Xingyao Wang 1e50d58982 fix(controller): "agent task already running" (#1516)
* fix agent task already running bug

* attemp to fix "Current task state not recognized"

* Revert "attemp to fix "Current task state not recognized""

This reverts commit f5cbfe1ebb.

---------

Co-authored-by: Leo <ifuryst@gmail.com>
2024-05-02 11:33:51 -04:00
Shimada666 24750ba04f fix: update correct troubleshooting doc url (#1510) 2024-05-02 14:09:05 +00:00
sp.wack cc4879a973 refactor tests to pass since changes, remove console logs, use toast error (#1497) 2024-05-02 09:46:20 -04:00
Boxuan Li 11d8253215 Add new CommitWriterAgent to auto-generate commit messages from staged diffs (#1484)
* Add new CommitWriterAgent to auto-generate commit messages from staged diffs

This commit introduces the CommitWriterAgent along with its configuration and detailed task description. The agent is designed to analyze git diffs staged for commit and automatically generate succinct and relevant commit messages.

* Remove devnote section from yaml and add README
2024-05-02 09:42:55 -04:00
Robert Brennan 0bdbd8a90d disable action concurrency (#1503)
* disable action concurrency

* empty commit
2024-05-02 13:30:04 +00:00
Robert Brennan 8dbbbcf1ba dont run lint on main (#1518) 2024-05-02 09:11:11 -04:00
Boxuan Li 30e10e7a4a Micro-agent: Remove planner specific actions (#1515) 2024-05-02 08:01:58 -04:00
Leo 95e4ca490f Feat: add lint frontend and lint all to Makefile. (#1354)
* Feat: add lint frontend and lint all to Makefile.

* style codes.

* Remove redundant target.

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-05-02 11:53:57 +00:00
Frank Xu 836864fa88 [feat] Integrate BrowserGym (#1452)
* add a single-threaded server serving browsergym

* update poetry

* update browser page content

* add import to make sure browsergym environments are registered properly

* remove flask server, use multiprocess impl and Pipe

* fix

* refactor BrowserEnv

* update browser action and obs to include more complete info

* fix screenshot

* update poetry lock

* add playwright install to workflow

* update

* add better html to text conversion

* update for better text conversion to maintain parity with the current handling of browseurlaction

* update

* update poetry

* update multiprocessing mp

* fix multiprocessing

* update

* update github workflow

---------

Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
2024-05-02 19:52:53 +08:00
Robert Brennan 0d77f495e3 Add 404 troubleshooting section (#1505) 2024-05-01 21:22:20 -07:00
Robert Brennan 24cc8ceb96 add back free storage (#1506) 2024-05-02 02:44:10 +00:00
Jirka Borovec 1b810cfbf0 ci/lint: fix calling Ruff's format (#1457)
* ci/lint: fix calling Ruff's format

* Transition for ruff lint. Only checking the modified files.

---------

Co-authored-by: ifuryst <ifuryst@gmail.com>
2024-05-01 22:19:54 -04:00
Robert Brennan cfef3ee5c4 Fix docker push for non-forks (#1499)
* fix fork check

* minor docker tweaks

* remove caching

* try not chowinng

* fix chowns

* revert build.sh

* fix entrypoint user

* change message

* remove free disk space

* chown the entrypoint

* remove comments

* empty commit
2024-05-02 10:15:12 +08:00
Xingyao Wang 435f47ca0e Improve the both frontend and backend for CodeActAgent (#1494)
* improve the both frontend and backend for CodeActAgent

* fix linter

* update integration test
2024-05-02 02:07:40 +08:00
267 changed files with 3669 additions and 5235 deletions
+8 -5
View File
@@ -12,9 +12,6 @@ jobs:
build:
name: Build Docusaurus
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs
steps:
- uses: actions/checkout@v4
with:
@@ -24,11 +21,17 @@ jobs:
node-version: 18
cache: npm
cache-dependency-path: docs/package-lock.json
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Generate Python Docs
run: rm -rf docs/modules/python && pip install pydoc-markdown && pydoc-markdown
- name: Install dependencies
run: npm ci
run: cd docs && npm ci
- name: Build website
run: npm run build
run: cd docs && npm run build
- name: Upload Build Artifact
if: github.ref == 'refs/heads/main'
+11 -2
View File
@@ -1,6 +1,14 @@
name: Run e2e test with dummy agent
on: [push]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
pull_request:
jobs:
test:
@@ -15,7 +23,8 @@ jobs:
run: |
curl -sSL https://install.python-poetry.org | python3 -
poetry install --without evaluation
poetry run playwright install --with-deps chromium
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
poetry run python opendevin/core/main.py -t "do a flip" -m ollama/not-a-model -d ./workspace/ -c DummyAgent
+22 -16
View File
@@ -1,9 +1,15 @@
name: Publish Docker Image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
tags:
- '*'
pull_request:
workflow_dispatch:
inputs:
@@ -28,20 +34,6 @@ jobs:
- name: checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
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:
@@ -58,13 +50,27 @@ jobs:
docker-images: false
swap-storage: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
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: Build and push ${{ matrix.image }}
if: github.event.pull_request.head.repo.full_name == github.repository
if: "!github.event.pull_request.head.repo.fork"
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
if: "github.event.pull_request.head.repo.fork"
run: |
./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }}
+10 -1
View File
@@ -1,5 +1,9 @@
name: Lint
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
@@ -33,6 +37,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up python
uses: actions/setup-python@v5
with:
@@ -41,8 +47,11 @@ jobs:
- name: Install pre-commit
run: pip install pre-commit==3.7.0
- name: Run pre-commit hooks
if: github.ref != 'refs/heads/main'
run: |
git fetch https://github.com/OpenDevin/OpenDevin.git main:main && \
pre-commit run \
--all-files \
--files \
$(git diff --name-only $(git merge-base main $(git branch --show-current)) $(git branch --show-current) | tr '\n' ' ') \
--show-diff-on-failure \
--config ./dev_config/python/.pre-commit-config.yaml
+1 -1
View File
@@ -49,7 +49,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
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
rm task.txt
- name: Check if review file is non-empty
@@ -1,5 +1,9 @@
name: Run Integration Tests
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
+4
View File
@@ -1,5 +1,9 @@
name: Run Unit Tests
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
+1 -1
View File
@@ -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
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
rm task.txt
- name: Setup Git, Create Branch, and Commit Changes
+18 -9
View File
@@ -148,9 +148,24 @@ install-precommit-hooks:
@poetry run pre-commit install --config $(PRECOMMIT_CONFIG_PATH)
@echo "$(GREEN)Pre-commit hooks installed successfully.$(RESET)"
lint:
lint-backend:
@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 $$(git diff --name-only $$(git merge-base main $$(git branch --show-current)) $$(git branch --show-current) | tr '\n' ' ') --show-diff-on-failure --config $(PRECOMMIT_CONFIG_PATH)
lint-frontend:
@echo "$(YELLOW)Running linters for frontend...$(RESET)"
@cd frontend && npm run lint
lint:
@$(MAKE) -s lint-frontend
@$(MAKE) -s lint-backend
test-frontend:
@echo "$(YELLOW)Running tests for frontend...$(RESET)"
@cd frontend && npm run test
test:
@$(MAKE) -s test-frontend
build-frontend:
@echo "$(YELLOW)Building frontend...$(RESET)"
@@ -159,7 +174,7 @@ 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) --reload --reload-exclude "workspace/*"
# Start frontend
start-frontend:
@@ -235,12 +250,6 @@ clean:
@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)"
+30 -5
View File
@@ -24,21 +24,24 @@
<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>
<a href="https://join.slack.com/t/opendevin/shared_invite/zt-2i1iqdag6-bVmvamiPA9EZUu7oCO6KhA"><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/ESHStjSjD4"><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="./docs/static/img/logo.png" alt="Logo" width="200" height="200">
<h1 align="center">OpenDevin: Code Less, Make More</h1>
<a href="https://opendevin.github.io/OpenDevin/"><img src="https://img.shields.io/badge/Documenation-OpenDevin-blue?logo=googledocs&logoColor=white&style=for-the-badge" alt="Check out the documentation"></a>
</div>
## 🎯 Mission
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/).
## [🚀 Get Started](https://opendevin.github.io/OpenDevin/modules/usage/intro)
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;">
@@ -46,6 +49,28 @@ To learn more and to use OpenDevin, check out our [documentation](https://opende
</a>
</p>
## ⚡ Quick Start
You can run OpenDevin with Docker. It works best with the most recent
version of Docker, `26.0.0`.
```bash
#The directory you want OpenDevin to modify. MUST be an absolute path!
export WORKSPACE_BASE=$(pwd)/workspace;
docker run \
--pull=always \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-v $WORKSPACE_BASE:/opt/workspace_base \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 3000:3000 \
--add-host host.docker.internal:host-gateway \
ghcr.io/opendevin/opendevin:0.5
```
For troubleshooting and advanced configuration, see
[the full documentation](https://opendevin.github.io/OpenDevin/).
## 🤝 How to Contribute
OpenDevin is a community-driven project, and we welcome contributions from everyone. Whether you're a developer, a researcher, or simply enthusiastic about advancing the field of software engineering with AI, there are many ways to get involved:
@@ -66,8 +91,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-2ggtwn3k5-PvAA2LUmqGHVZ~XzGq~ILw)
- [Discord server](https://discord.gg/ESHStjSjD4)
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!
+1 -1
View File
@@ -1,4 +1,4 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import SWEAgent
+13 -17
View File
@@ -1,15 +1,15 @@
from typing import List
from opendevin.action import (
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Action,
AgentThinkAction,
FileReadAction,
FileWriteAction,
)
from opendevin.agent import Agent
from opendevin.events.observation import Observation
from opendevin.llm.llm import LLM
from opendevin.observation import Observation
from opendevin.state import State
from .parser import parse_command
from .prompts import (
@@ -48,9 +48,12 @@ class SWEAgent(Agent):
)
action_resp = resp['choices'][0]['message']['content']
print(f"\033[1m\033[91m{resp['usage']}\033[0m")
print('\n==== RAW OUTPUT ====',
f'\033[96m{action_resp}\033[0m',
'==== END RAW ====\n', sep='\n')
print(
'\n==== RAW OUTPUT ====',
f'\033[96m{action_resp}\033[0m',
'==== END RAW ====\n',
sep='\n',
)
return parse_command(action_resp, self.cur_file, self.cur_line)
def _update(self, action: Action) -> None:
@@ -68,22 +71,15 @@ class SWEAgent(Agent):
for prev_action, obs in state.updated_info:
self._remember(prev_action, obs)
prompt = STEP_PROMPT(
state.plan.main_goal,
self.cur_file,
self.cur_line
)
prompt = STEP_PROMPT(state.plan.main_goal, self.cur_file, self.cur_line)
msgs = [
{'content': SYSTEM_MESSAGE, 'role': 'system'},
{'content': prompt, 'role': 'user'}
{'content': prompt, 'role': 'user'},
]
if len(self.running_memory) > 0:
context = CONTEXT_PROMPT(
self.running_memory,
self.memory_window
)
context = CONTEXT_PROMPT(self.running_memory, self.memory_window)
msgs.insert(1, {'content': context, 'role': 'user'})
# clrs = [''] * (len(msgs)-2) + ['\033[0;36m', '\033[0;35m']
# print('\n\n'.join([c+m['content']+'\033[0m' for c, m in zip(clrs, msgs)]))
+1 -1
View File
@@ -1,6 +1,6 @@
import re
from opendevin.action import (
from opendevin.events.action import (
Action,
AgentEchoAction,
AgentFinishAction,
+17 -10
View File
@@ -1,6 +1,6 @@
from dotenv import load_dotenv
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .micro.agent import MicroAgent
from .micro.registry import all_microagents
@@ -8,7 +8,6 @@ from .micro.registry import all_microagents
load_dotenv()
from . import ( # noqa: E402
SWE_agent,
codeact_agent,
@@ -18,18 +17,26 @@ from . import ( # noqa: E402
planner_agent,
)
__all__ = ['monologue_agent', 'codeact_agent',
'planner_agent', 'SWE_agent',
'delegator_agent',
'dummy_agent']
__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,
})
anon_class = type(
name,
(MicroAgent,),
{
'prompt': prompt,
'agent_definition': agent,
},
)
Agent.register(name, anon_class)
+20 -14
View File
@@ -1,23 +1,29 @@
# CodeAct-based Agent Framework
# CodeAct Agent Framework
This folder implements the [CodeAct idea](https://arxiv.org/abs/2402.13463) that relies on LLM to autonomously perform actions in a Bash shell. It requires more from the LLM itself: LLM needs to be capable enough to do all the stuff autonomously, instead of stuck in an infinite loop.
This folder implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.13463), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents **act**ions into a unified **code** action space for both *simplicity* and *performance* (see paper for more details).
**NOTE: This agent is still highly experimental and under active development to reach the capability described in the original paper & [repo](https://github.com/xingyaoww/code-act).**
The conceptual idea is illustrated below. At each turn, the agent can:
<video src="https://github.com/xingyaoww/code-act/assets/38853559/62c80ada-62ce-447e-811c-fc801dd4beac"> </video>
*Demo of the expected capability - work-in-progress.*
1. **Converse**: Communicate with humans in natural language to ask for clarification, confirmation, etc.
2. **CodeAct**: Choose to perform the task by executing code
- Execute any valid Linux `bash` command
- Execute any valid `Python` code with [an interactive Python interpreter](https://ipython.org/). This is simulated through `bash` command, see plugin system below for more details.
```bash
mkdir workspace
PYTHONPATH=`pwd`:$PYTHONPATH python3 opendevin/main.py -d ./workspace -c CodeActAgent -t "Please write a flask app that returns 'Hello, World\!' at the root URL, then start the app on port 5000. python3 has already been installed for you."
```
![image](https://github.com/OpenDevin/OpenDevin/assets/38853559/92b622e3-72ad-4a61-8f41-8c040b6d5fb3)
Example: prompts `gpt-4-0125-preview` to write a flask server, install `flask` library, and start the server.
## Plugin System
<img width="951" alt="image" src="https://github.com/OpenDevin/OpenDevin/assets/38853559/325c3115-a343-4cc5-a92b-f1e5d552a077">
To make the CodeAct agent more powerful with only access to `bash` action space, CodeAct agent leverages OpenDevin's plugin system:
- [Jupyter plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/jupyter): for IPython execution via bash command
- [SWE-agent tool plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/swe_agent_commands): Powerful bash command line tools for software development tasks introduced by [swe-agent](https://github.com/princeton-nlp/swe-agent).
<img width="957" alt="image" src="https://github.com/OpenDevin/OpenDevin/assets/38853559/68ad10c1-744a-4e9d-bb29-0f163d665a0a">
## Demo
Most of the things are working as expected, except at the end, the model did not follow the instruction to stop the interaction by outputting `<execute> exit </execute>` as instructed.
https://github.com/OpenDevin/OpenDevin/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac
**TODO**: This should be fixable by either (1) including a complete in-context example like [this](https://github.com/xingyaoww/mint-bench/blob/main/mint/tasks/in_context_examples/reasoning/with_tool.txt), OR (2) collect some interaction data like this and fine-tune a model (like [this](https://github.com/xingyaoww/code-act), a more complex route).
*Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)*
## Work-in-progress & Next step
[] Support web-browsing
[] Complete the workflow for CodeAct agent to submit Github PRs
+1 -1
View File
@@ -1,4 +1,4 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .codeact_agent import CodeActAgent
+77 -23
View File
@@ -2,7 +2,9 @@ import re
from typing import List, Mapping
from agenthub.codeact_agent.prompt import EXAMPLES, SYSTEM_MESSAGE
from opendevin.action import (
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Action,
AgentEchoAction,
AgentFinishAction,
@@ -11,20 +13,18 @@ from opendevin.action import (
IPythonRunCellAction,
NullAction,
)
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.observation import (
from opendevin.events.observation import (
AgentMessageObservation,
CmdOutputObservation,
IPythonRunCellObservation,
UserMessageObservation,
)
from opendevin.sandbox.plugins import (
from opendevin.llm.llm import LLM
from opendevin.runtime.plugins import (
JupyterRequirement,
PluginRequirement,
SWEAgentCommandsRequirement,
)
from opendevin.state import State
def parse_response(response) -> str:
@@ -35,25 +35,73 @@ def parse_response(response) -> str:
return action
def truncate_observation(observation: str, max_chars: int = 5000) -> str:
"""
Truncate the middle of the observation if it is too long.
"""
if len(observation) <= max_chars:
return observation
half = max_chars // 2
return (
observation[:half]
+ '\n[... Observation truncated due to length ...]\n'
+ observation[-half:]
)
class CodeActAgent(Agent):
"""
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.
### Overview
This agent implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.13463), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents **act**ions into a unified **code** action space for both *simplicity* and *performance* (see paper for more details).
The conceptual idea is illustrated below. At each turn, the agent can:
1. **Converse**: Communicate with humans in natural language to ask for clarification, confirmation, etc.
2. **CodeAct**: Choose to perform the task by executing code
- Execute any valid Linux `bash` command
- Execute any valid `Python` code with [an interactive Python interpreter](https://ipython.org/). This is simulated through `bash` command, see plugin system below for more details.
![image](https://github.com/OpenDevin/OpenDevin/assets/38853559/92b622e3-72ad-4a61-8f41-8c040b6d5fb3)
### Plugin System
To make the CodeAct agent more powerful with only access to `bash` action space, CodeAct agent leverages OpenDevin's plugin system:
- [Jupyter plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/jupyter): for IPython execution via bash command
- [SWE-agent tool plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/swe_agent_commands): Powerful bash command line tools for software development tasks introduced by [swe-agent](https://github.com/princeton-nlp/swe-agent).
### Demo
https://github.com/OpenDevin/OpenDevin/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac
*Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)*
### Work-in-progress & Next step
[] Support web-browsing
[] Complete the workflow for CodeAct agent to submit Github PRs
"""
sandbox_plugins: List[PluginRequirement] = [JupyterRequirement(), SWEAgentCommandsRequirement()]
sandbox_plugins: List[PluginRequirement] = [
JupyterRequirement(),
SWEAgentCommandsRequirement(),
]
SUPPORTED_ACTIONS = (
CmdRunAction,
IPythonRunCellAction,
AgentEchoAction,
AgentTalkAction,
NullAction
NullAction,
)
SUPPORTED_OBSERVATIONS = (
AgentMessageObservation,
UserMessageObservation,
CmdOutputObservation,
IPythonRunCellObservation
IPythonRunCellObservation,
)
def __init__(
@@ -71,18 +119,17 @@ class CodeActAgent(Agent):
def step(self, state: State) -> Action:
"""
Performs one step using the Code Act Agent.
Performs one step using the CodeAct Agent.
This includes gathering info on previous steps and prompting the model to make a command to execute.
Parameters:
- state (State): used to get updated info and background commands
Returns:
- CmdRunAction(command) - command action to run
- AgentEchoAction(content=INVALID_INPUT_MESSAGE) - invalid command output
Raises:
- NotImplementedError - for actions other than CmdOutputObservation or AgentMessageObservation
- CmdRunAction(command) - bash command to run
- IPythonRunCellAction(code) - IPython code to run
- AgentTalkAction(content) - Talk action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""
if len(self.messages) == 0:
@@ -94,7 +141,7 @@ class CodeActAgent(Agent):
'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}"
)
),
},
]
updated_info = state.updated_info
@@ -110,24 +157,27 @@ class CodeActAgent(Agent):
obs, self.SUPPORTED_OBSERVATIONS
), f'{obs.__class__} is not supported (supported: {self.SUPPORTED_OBSERVATIONS})'
if isinstance(obs, (AgentMessageObservation, UserMessageObservation)):
self.messages.append(
{'role': 'user', 'content': obs.content})
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 = 'OBSERVATION:\n' + truncate_observation(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'
splited[i] = (
'![image](data:image/png;base64, ...) already displayed to user'
)
content = '\n'.join(splited)
content = truncate_observation(content)
self.messages.append({'role': 'user', 'content': content})
else:
raise NotImplementedError(
@@ -140,7 +190,7 @@ class CodeActAgent(Agent):
'</execute_ipython>',
'</execute_bash>',
],
temperature=0.0
temperature=0.0,
)
action_str: str = parse_response(response)
state.num_of_chars += sum(
@@ -148,7 +198,9 @@ class CodeActAgent(Agent):
) + 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):
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()
# a command was found
@@ -156,7 +208,9 @@ class CodeActAgent(Agent):
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):
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()
+3 -3
View File
@@ -1,9 +1,9 @@
from opendevin.sandbox.plugins import SWEAgentCommandsRequirement
from opendevin.runtime.plugins import SWEAgentCommandsRequirement
_SWEAGENT_BASH_DOCS = '\n'.join(
filter(
lambda x: not x.startswith('submit'),
SWEAgentCommandsRequirement.documentation.split('\n')
SWEAgentCommandsRequirement.documentation.split('\n'),
)
)
# _SWEAGENT_BASH_DOCS content below:
@@ -40,9 +40,9 @@ The assistant should stop <execute> and provide an answer when they have already
{_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.
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
"""
EXAMPLES = """
+1 -1
View File
@@ -1,4 +1,4 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import DelegatorAgent
+34 -21
View File
@@ -1,10 +1,10 @@
from typing import List
from opendevin.action import Action, AgentDelegateAction, AgentFinishAction
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import Action, AgentDelegateAction, AgentFinishAction
from opendevin.events.observation import AgentDelegateObservation
from opendevin.llm.llm import LLM
from opendevin.observation import AgentDelegateObservation
from opendevin.state import State
class DelegatorAgent(Agent):
@@ -12,6 +12,7 @@ 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):
@@ -37,34 +38,46 @@ class DelegatorAgent(Agent):
"""
if self.current_delegate == '':
self.current_delegate = 'study'
return AgentDelegateAction(agent='StudyRepoForTaskAgent', inputs={
'task': state.plan.main_goal
})
return AgentDelegateAction(
agent='StudyRepoForTaskAgent', inputs={'task': state.plan.main_goal}
)
lastObservation = state.history[-1][1]
if not isinstance(lastObservation, AgentDelegateObservation):
last_observation = state.history[-1][1]
if not isinstance(last_observation, 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'],
})
return AgentDelegateAction(
agent='CoderAgent',
inputs={
'task': state.plan.main_goal,
'summary': last_observation.outputs['summary'],
},
)
elif self.current_delegate == 'coder':
self.current_delegate = 'verifier'
return AgentDelegateAction(agent='Verifier', inputs={
'task': state.plan.main_goal,
})
return AgentDelegateAction(
agent='VerifierAgent',
inputs={
'task': state.plan.main_goal,
},
)
elif self.current_delegate == 'verifier':
if 'completed' in lastObservation.outputs and lastObservation.outputs['completed']:
if (
'completed' in last_observation.outputs
and last_observation.outputs['completed']
):
return AgentFinishAction()
else:
self.current_delegate = 'coder'
return AgentDelegateAction(agent='Coder', inputs={
'task': state.plan.main_goal,
'summary': lastObservation.outputs['summary'],
})
return AgentDelegateAction(
agent='CoderAgent',
inputs={
'task': state.plan.main_goal,
'summary': last_observation.outputs['summary'],
},
)
else:
raise Exception('Invalid delegate state')
+1 -1
View File
@@ -1,4 +1,4 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import DummyAgent
+95 -54
View File
@@ -1,7 +1,9 @@
import time
from typing import List, TypedDict
from opendevin.action import (
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Action,
AddTaskAction,
AgentFinishAction,
@@ -13,9 +15,7 @@ from opendevin.action import (
FileWriteAction,
ModifyTaskAction,
)
from opendevin.agent import Agent
from opendevin.llm.llm import LLM
from opendevin.observation import (
from opendevin.events.observation import (
AgentRecallObservation,
CmdOutputObservation,
FileReadObservation,
@@ -23,7 +23,7 @@ from opendevin.observation import (
NullObservation,
Observation,
)
from opendevin.state import State
from opendevin.llm.llm import LLM
"""
FIXME: There are a few problems this surfaced
@@ -33,7 +33,9 @@ FIXME: There are a few problems this surfaced
* Browser not working
"""
ActionObs = TypedDict('ActionObs', {'action': Action, 'observations': List[Observation]})
ActionObs = TypedDict(
'ActionObs', {'action': Action, 'observations': List[Observation]}
)
BACKGROUND_CMD = 'echo "This is in the background" && sleep .1 && echo "This too"'
@@ -46,51 +48,82 @@ class DummyAgent(Agent):
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': [],
}]
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', # type: ignore[arg-type]
command=BACKGROUND_CMD,
),
CmdOutputObservation(
'This is in the background\nThis too\n',
command_id='42', # type: ignore[arg-type]
command=BACKGROUND_CMD,
),
],
},
{
'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)
@@ -102,16 +135,24 @@ class DummyAgent(Agent):
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:
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:
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}'
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]:
@@ -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 +1,2 @@
* `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:
* `finish` - if 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,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.
+6 -5
View File
@@ -3,11 +3,11 @@ 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.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.core.exceptions import LLMOutputError
from opendevin.events.action import Action, action_from_dict
from opendevin.llm.llm import LLM
from opendevin.state import State
from .instructions import instructions
from .registry import all_microagents
@@ -65,7 +65,8 @@ class MicroAgent(Agent):
state=state,
instructions=instructions,
to_json=to_json,
delegates=self.delegates)
delegates=self.delegates,
)
messages = [{'content': prompt, 'role': 'user'}]
resp = self.llm.completion(messages=messages)
action_resp = resp['choices'][0]['message']['content']
+25
View File
@@ -0,0 +1,25 @@
## Introduction
CommitWriterAgent can help write git commit message. Example:
```bash
WORKSPACE_MOUNT_PATH="`PWD`" SANDBOX_TYPE="exec" \
poetry run python opendevin/core/main.py -t "dummy task" -c CommitWriterAgent -d ./
```
This agent is special in the sense that it doesn't need a task. Once called,
it attempts to read all diff in the git staging area and write a good commit
message.
## Future work
### Feedback loop
The commit message could be (optionally) shown to the customer or
other agents, so that CommitWriterAgent could gather feedback to further
improve the commit message.
### Task rejection
When the agent cannot compile a commit message (e.g. not git repository), it
should reject the task with an explanation.
+6
View File
@@ -0,0 +1,6 @@
name: CommitWriterAgent
description: "Write a git commit message for files in the git staging area"
generates: Action
inputs: {}
outputs:
answer: string
+28
View File
@@ -0,0 +1,28 @@
# Task
You are a responsible software engineer and always write good commit messages.
Please analyze the diff in the staging area, understand the context and content
of the updates from the diff only. Identify key elements like:
- Which files are affected?
- What types of changes were made (e.g., new features, bug fixes, refactoring, documentation, testing)?
Then you should generate a commit message that succinctly summarizes the staged
changes. The commit message should include:
- A summary line that clearly states the purpose of the changes.
- Optionally, a detailed description if the changes are complex or need further explanation.
You should find the diff using `git diff --cached`, compile a commit message,
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.run }}
{{ instructions.actions.finish }}
## Format
{{ instructions.format.action }}
+10 -7
View File
@@ -7,13 +7,16 @@ 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]
if root == base_dir:
obj = instructions
else:
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:
+1 -1
View File
@@ -1,5 +1,5 @@
# Task
You are a brilliant mathematician and programmer. You've been given the follwoing problem to solve:
You are a brilliant mathematician and programmer. You've been given the following problem to solve:
{{ state.plan.main_goal }}
+1 -1
View File
@@ -14,7 +14,7 @@ the information needed to complete this task:
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.
codebase are relevant to the task, including particular files, functions, and classes.
When you're done, put your summary in `outputs.summary` in the `finish` action.
## History
+1 -1
View File
@@ -1,4 +1,4 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import MonologueAgent
+24 -17
View File
@@ -2,8 +2,13 @@ from typing import List
import agenthub.monologue_agent.utils.prompts as prompts
from agenthub.monologue_agent.utils.monologue import Monologue
from opendevin import config
from opendevin.action import (
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.core import config
from opendevin.core.exceptions import AgentNoInstructionError
from opendevin.core.schema import ActionType
from opendevin.core.schema.config import ConfigType
from opendevin.events.action import (
Action,
AgentRecallAction,
AgentThinkAction,
@@ -14,10 +19,7 @@ from opendevin.action import (
GitHubPushAction,
NullAction,
)
from opendevin.agent import Agent
from opendevin.exceptions import AgentNoInstructionError
from opendevin.llm.llm import LLM
from opendevin.observation import (
from opendevin.events.observation import (
AgentRecallObservation,
BrowserOutputObservation,
CmdOutputObservation,
@@ -25,14 +27,12 @@ from opendevin.observation import (
NullObservation,
Observation,
)
from opendevin.schema import ActionType
from opendevin.schema.config import ConfigType
from opendevin.state import State
from opendevin.llm.llm import LLM
if config.get(ConfigType.AGENT_MEMORY_ENABLED):
from agenthub.monologue_agent.utils.memory import LongTermMemory
MAX_MONOLOGUE_LENGTH = 20000
MAX_TOKEN_COUNT_PADDING = 512
MAX_OUTPUT_LENGTH = 5000
INITIAL_THOUGHTS = [
@@ -56,7 +56,7 @@ INITIAL_THOUGHTS = [
'RUN echo "hello world"',
'hello world',
'Cool! I bet I can write files too using the write action.',
"WRITE echo \"console.log('hello world')\" > test.js",
'WRITE echo "console.log(\'hello world\')" > test.js',
'',
"I just created test.js. I'll try and run it now.",
'RUN node test.js',
@@ -114,8 +114,6 @@ class MonologueAgent(Agent):
- event (dict): The event that will be added to monologue and memory
"""
if 'extras' in event and 'screenshot' in event['extras']:
del event['extras']['screenshot']
if (
'args' in event
and 'output' in event['args']
@@ -128,12 +126,22 @@ class MonologueAgent(Agent):
self.monologue.add_event(event)
if self.memory is not None:
self.memory.add_event(event)
if self.monologue.get_total_length() > MAX_MONOLOGUE_LENGTH:
# Test monologue token length
prompt = prompts.get_request_action_prompt(
'',
self.monologue.get_thoughts(),
[],
)
messages = [{'content': prompt, 'role': 'user'}]
token_count = self.llm.get_token_count(messages)
if token_count + MAX_TOKEN_COUNT_PADDING > self.llm.max_input_tokens:
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
Utilizes the INITIAL_THOUGHTS list to give the agent a context for its capabilities
and how to navigate the WORKSPACE_MOUNT_PATH_IN_SANDBOX in `config` (e.g., /workspace by default).
Short circuited to return when already initialized.
Will execute again when called after reset.
@@ -173,8 +181,7 @@ class MonologueAgent(Agent):
elif previous_action == ActionType.READ:
observation = FileReadObservation(content=thought, path='')
elif previous_action == ActionType.RECALL:
observation = AgentRecallObservation(
content=thought, memories=[])
observation = AgentRecallObservation(content=thought, memories=[])
elif previous_action == ActionType.BROWSE:
observation = BrowserOutputObservation(
content=thought, url='', screenshot=''
+34 -16
View File
@@ -13,9 +13,9 @@ from tenacity import (
wait_random_exponential,
)
from opendevin import config
from opendevin.logger import opendevin_logger as logger
from opendevin.schema.config import ConfigType
from opendevin.core import config
from opendevin.core.logger import opendevin_logger as logger
from opendevin.core.schema.config import ConfigType
from . import json
@@ -37,15 +37,22 @@ else:
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)
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)
@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)
@@ -56,9 +63,16 @@ embedding_strategy = config.get(ConfigType.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']
supported_ollama_embed_models = [
'llama2',
'mxbai-embed-large',
'nomic-embed-text',
'all-minilm',
'stable-code',
]
if embedding_strategy in supported_ollama_embed_models:
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),
@@ -66,16 +80,20 @@ if embedding_strategy in supported_ollama_embed_models:
)
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(ConfigType.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),
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),
@@ -87,9 +105,8 @@ elif (embedding_strategy is not None) and (embedding_strategy.lower() == 'none')
embed_model = None
else:
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
embed_model = HuggingFaceEmbedding(
model_name='BAAI/bge-small-en-v1.5'
)
embed_model = HuggingFaceEmbedding(model_name='BAAI/bge-small-en-v1.5')
sema = threading.Semaphore(value=config.get(ConfigType.AGENT_MEMORY_MAX_THREADS))
@@ -109,7 +126,8 @@ class LongTermMemory:
self.collection = db.get_or_create_collection(name='memories')
vector_store = ChromaVectorStore(chroma_collection=self.collection)
self.index = VectorStoreIndex.from_vector_store(
vector_store, embed_model=embed_model)
vector_store, embed_model=embed_model
)
self.thought_idx = 0
self._add_threads = []
+2 -3
View File
@@ -1,9 +1,8 @@
import agenthub.monologue_agent.utils.json as json
import agenthub.monologue_agent.utils.prompts as prompts
from opendevin.exceptions import AgentEventTypeError
from opendevin.core.exceptions import AgentEventTypeError
from opendevin.core.logger import opendevin_logger as logger
from opendevin.llm.llm import LLM
from opendevin.logger import opendevin_logger as logger
class Monologue:
+17 -9
View File
@@ -2,16 +2,16 @@ import re
from json import JSONDecodeError
from typing import List
from opendevin import config
from opendevin.action import (
from opendevin.core import config
from opendevin.core.exceptions import LLMOutputError
from opendevin.core.schema.config import ConfigType
from opendevin.events.action import (
Action,
action_from_dict,
)
from opendevin.exceptions import LLMOutputError
from opendevin.observation import (
from opendevin.events.observation import (
CmdOutputObservation,
)
from opendevin.schema.config import ConfigType
from . import json
@@ -158,7 +158,9 @@ def get_request_action_prompt(
'hint': hint,
'user': user,
'timeout': config.get(ConfigType.SANDBOX_TIMEOUT),
'WORKSPACE_MOUNT_PATH_IN_SANDBOX': config.get(ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX),
'WORKSPACE_MOUNT_PATH_IN_SANDBOX': config.get(
ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX
),
}
@@ -178,12 +180,18 @@ def parse_action_response(response: str) -> Action:
# Find response-looking json in the output and use the more promising one. Helps with weak llms
response_json_matches = re.finditer(
r"""{\s*\"action\":\s?\"(\w+)\"(?:,?|,\s*\"args\":\s?{((?:.|\s)*?)})\s*}""",
response) # Find all response-looking strings
response,
) # Find all response-looking strings
def rank(match):
return len(match[2]) if match[1] == 'think' else 130 # Crudely rank multiple responses by length
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
action_dict = json.loads(
max(response_json_matches, key=rank)[0]
) # Use the highest ranked response
except (ValueError, JSONDecodeError):
raise LLMOutputError(
'Invalid JSON, the response must be well-formed JSON as specified in the prompt.'
+1 -1
View File
@@ -1,4 +1,4 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import PlannerAgent
+3 -3
View File
@@ -1,9 +1,9 @@
from typing import List
from opendevin.action import Action, AgentFinishAction
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import Action, AgentFinishAction
from opendevin.llm.llm import LLM
from opendevin.state import State
from .prompt import get_prompt, parse_response
+6 -11
View File
@@ -1,7 +1,10 @@
import json
from typing import Dict, List, Tuple, Type
from opendevin.action import (
from opendevin.controller.state.plan import Plan
from opendevin.core.logger import opendevin_logger as logger
from opendevin.core.schema import ActionType
from opendevin.events.action import (
Action,
AddTaskAction,
AgentFinishAction,
@@ -17,13 +20,10 @@ from opendevin.action import (
NullAction,
action_from_dict,
)
from opendevin.logger import opendevin_logger as logger
from opendevin.observation import (
from opendevin.events.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,
@@ -131,7 +131,7 @@ 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 """
"""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.",
@@ -172,11 +172,6 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
latest_action = action
if not isinstance(observation, NullObservation):
observation_dict = observation.to_memory()
if (
'extras' in observation_dict
and 'screenshot' in observation_dict['extras']
):
del observation_dict['extras']['screenshot']
history_dicts.append(observation_dict)
history_str = json.dumps(history_dicts, indent=2)
current_task = plan.get_current_task()
+20 -26
View File
@@ -5,7 +5,7 @@ WORKDIR /app
COPY ./frontend/package.json frontend/package-lock.json ./
RUN npm install -g npm@10.5.1
RUN npm install
RUN npm ci
COPY ./frontend ./
RUN npm run make-i18n && npm run build
@@ -33,7 +33,8 @@ FROM python:3.12-slim as runtime
WORKDIR /app
ENV RUN_AS_DEVIN=true
ENV SANDBOX_USER_ID=1000
# A random number--we need this to be different from the user's UID on the host machine
ENV OPENDEVIN_USER_ID=42420
ENV USE_HOST_NETWORK=false
ENV SSH_HOSTNAME=host.docker.internal
ENV WORKSPACE_BASE=/opt/workspace_base
@@ -43,40 +44,33 @@ 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 && \
RUN sed -i 's/^UID_MIN.*/UID_MIN 499/' /etc/login.defs # Default is 1000, but OSX is often 501
RUN groupadd app
RUN useradd -l -m -u $OPENDEVIN_USER_ID -s /bin/bash opendevin && \
usermod -aG app opendevin && \
usermod -aG sudo opendevin && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN chown -R opendevin:opendevin /app
RUN chown -R opendevin:app /app && chmod -R 770 /app
USER opendevin
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
COPY --chown=opendevin:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
RUN playwright install --with-deps chromium
COPY --from=frontend-builder /app/dist ./frontend/dist
COPY --chown=opendevin:app --chmod=770 ./opendevin ./opendevin
COPY --chown=opendevin:app --chmod=777 ./opendevin/runtime/plugins ./opendevin/runtime/plugins
COPY --chown=opendevin:app --chmod=770 ./agenthub ./agenthub
RUN python opendevin/core/download.py # No-op to download assets
RUN chown -R opendevin:app /app/logs && chmod -R 770 /app/logs # This gets created by the download.py script
COPY --chown=opendevin:app --chmod=770 --from=frontend-builder /app/dist ./frontend/dist
COPY --chown=opendevin:app --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
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"]
+21 -4
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# check user is root
if [ "$(id -u)" -ne 0 ]; then
echo "Please run as root"
echo "The OpenDevin entrypoint.sh must run as root"
exit 1
fi
@@ -10,14 +10,31 @@ if [ -z "$SANDBOX_USER_ID" ]; then
exit 1
fi
if [[ "$SANDBOX_USER_ID" -eq 0 ]]; then
echo "SANDBOX_USER_ID cannot be 0. Please run with a different user id."
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
if ! useradd -l -m -u $SANDBOX_USER_ID -s /bin/bash enduser; then
echo "Failed to create user enduser with id $SANDBOX_USER_ID. Moving opendevin user."
incremented_id=$(($SANDBOX_USER_ID + 1))
usermod -u $incremented_id opendevin
if ! useradd -l -m -u $SANDBOX_USER_ID -s /bin/bash enduser; then
echo "Failed to create user enduser with id $SANDBOX_USER_ID for a second time. Exiting."
exit 1
fi
fi
usermod -aG app enduser
mkdir -p /home/enduser/.cache/ms-playwright/
mv /home/opendevin/.cache/ms-playwright/ /home/enduser/.cache/
# 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
usermod -aG $DOCKER_SOCKET_GID enduser
# 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"
su enduser -c "cd /app && uvicorn opendevin.server.listen:app --host 0.0.0.0 --port 3000"
+1 -1
View File
@@ -18,7 +18,7 @@ cache_tag="$cache_tag_base"
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
if [[ $GITHUB_REF_NAME =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
major_version=$(echo $GITHUB_REF_NAME | cut -d. -f1)
minor_version=$(echo $GITHUB_REF_NAME | cut -d. -f1,2)
tags+=($major_version $minor_version)
+3 -12
View File
@@ -20,16 +20,16 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.3.7
rev: v0.4.1
hooks:
# Run the linter.
- id: ruff
entry: ruff check --config dev_config/python/ruff.toml opendevin/ agenthub/
entry: ruff check --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter]
args: [--fix]
# Run the formatter.
- id: ruff-format
entry: ruff check --config dev_config/python/ruff.toml opendevin/ agenthub/
entry: ruff format --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter]
- repo: https://github.com/pre-commit/mirrors-mypy
@@ -41,12 +41,3 @@ repos:
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
+3 -1
View File
@@ -15,7 +15,9 @@ ignore = [
"E501",
]
flake8-quotes = {inline-quotes = "single"}
[lint.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "single"
[format]
quote-style = "single"
+2 -2
View File
@@ -98,11 +98,11 @@ const config: Config = {
items: [
{
label: "Slack",
href: "https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw",
href: "https://join.slack.com/t/opendevin/shared_invite/zt-2ggtwn3k5-PvAA2LUmqGHVZ~XzGq~ILw"
},
{
label: "Discord",
href: "https://discord.gg/mBuDGRzzES",
href: "https://discord.gg/ESHStjSjD4",
},
],
},
@@ -1,34 +0,0 @@
---
sidebar_label: agent
title: agenthub.SWE_agent.agent
---
## SWEAgent Objects
```python
class SWEAgent(Agent)
```
An attempt to recreate swe_agent with output parsing, prompting style, and Application Computer Interface (ACI).
SWE-agent includes ACI functions like &#x27;goto&#x27;, &#x27;search_for&#x27;, &#x27;edit&#x27;, &#x27;scroll&#x27;, &#x27;run&#x27;
#### step
```python
def step(state: State) -> Action
```
SWE-Agent step:
1. Get context - past actions, custom commands, current step
2. Perform think-act - prompt model for action and reasoning
3. Catch errors - ensure model takes action (5 attempts max)
#### reset
```python
def reset() -> None
```
Used to reset the agent
@@ -1,34 +0,0 @@
---
sidebar_label: parser
title: agenthub.SWE_agent.parser
---
#### get\_action\_from\_string
```python
def get_action_from_string(command_string: str,
path: str,
line: int,
thoughts: str = '') -> Action | None
```
Parses the command string to find which command the agent wants to run
Converts the command into a proper Action and returns
#### parse\_command
```python
def parse_command(input_str: str, path: str, line: int)
```
Parses a given string and separates the command (enclosed in triple backticks) from any accompanying text.
**Arguments**:
- `input_str` _str_ - The input string to be parsed.
**Returns**:
- `tuple` - A tuple containing the command and the accompanying text (if any).
@@ -1,50 +0,0 @@
---
sidebar_label: codeact_agent
title: agenthub.codeact_agent.codeact_agent
---
## CodeActAgent Objects
```python
class CodeActAgent(Agent)
```
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.
#### \_\_init\_\_
```python
def __init__(llm: LLM) -> None
```
Initializes a new instance of the CodeActAgent class.
**Arguments**:
- llm (LLM): The llm to be used by this agent
#### step
```python
def step(state: State) -> Action
```
Performs one step using the Code Act Agent.
This includes gathering info on previous steps and prompting the model to make a command to execute.
**Arguments**:
- state (State): used to get updated info and background commands
**Returns**:
- CmdRunAction(command) - command action to run
- AgentEchoAction(content=INVALID_INPUT_MESSAGE) - invalid command output
**Raises**:
- NotImplementedError - for actions other than CmdOutputObservation or AgentMessageObservation
@@ -1,45 +0,0 @@
---
sidebar_label: agent
title: agenthub.delegator_agent.agent
---
## DelegatorAgent Objects
```python
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.
#### \_\_init\_\_
```python
def __init__(llm: LLM)
```
Initialize the Delegator Agent with an LLM
**Arguments**:
- llm (LLM): The llm to be used by this agent
#### step
```python
def step(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.
**Arguments**:
- state (State): The current state given the previous actions and observations
**Returns**:
- AgentFinishAction: If the last state was &#x27;completed&#x27;, &#x27;verified&#x27;, or &#x27;abandoned&#x27;
- Action: The next action to take based on llm response
@@ -1,14 +0,0 @@
---
sidebar_label: agent
title: agenthub.dummy_agent.agent
---
## DummyAgent Objects
```python
class DummyAgent(Agent)
```
The DummyAgent is used for e2e testing. It just sends the same set of actions deterministically,
without making any LLM calls.
@@ -1,30 +0,0 @@
---
sidebar_label: agent
title: agenthub.micro.agent
---
#### my\_encoder
```python
def my_encoder(obj)
```
Encodes objects as dictionaries
**Arguments**:
- obj (Object): An object that will be converted
**Returns**:
- dict: If the object can be converted it is returned in dict format
#### to\_json
```python
def to_json(obj, **kwargs)
```
Serialize an object to str format
@@ -1,62 +0,0 @@
---
sidebar_label: agent
title: agenthub.monologue_agent.agent
---
## MonologueAgent Objects
```python
class MonologueAgent(Agent)
```
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.
#### \_\_init\_\_
```python
def __init__(llm: LLM)
```
Initializes the Monologue Agent with an llm, monologue, and memory.
**Arguments**:
- llm (LLM): The llm to be used by this agent
#### step
```python
def step(state: State) -> Action
```
Modifies the current state by adding the most recent actions and observations, then prompts the model to think about it&#x27;s next action to take using monologue, memory, and hint.
**Arguments**:
- state (State): The current state based on previous steps taken
**Returns**:
- Action: The next action to take based on LLM response
#### search\_memory
```python
def search_memory(query: str) -> List[str]
```
Uses VectorIndexRetriever to find related memories within the long term memory.
Uses search to produce top 10 results.
**Arguments**:
- query (str): The query that we want to find related memories for
**Returns**:
- List[str]: A list of top 10 text results that matched the query
@@ -1,38 +0,0 @@
---
sidebar_label: json
title: agenthub.monologue_agent.utils.json
---
#### my\_encoder
```python
def my_encoder(obj)
```
Encodes objects as dictionaries
**Arguments**:
- obj (Object): An object that will be converted
**Returns**:
- dict: If the object can be converted it is returned in dict format
#### dumps
```python
def dumps(obj, **kwargs)
```
Serialize an object to str format
#### loads
```python
def loads(s, **kwargs)
```
Create a JSON object from str
@@ -1,52 +0,0 @@
---
sidebar_label: memory
title: agenthub.monologue_agent.utils.memory
---
## LongTermMemory Objects
```python
class LongTermMemory()
```
Responsible for storing information that the agent can call on later for better insights and context.
Uses chromadb to store and search through memories.
#### \_\_init\_\_
```python
def __init__()
```
Initialize the chromadb and set up ChromaVectorStore for later use.
#### add\_event
```python
def add_event(event: dict)
```
Adds a new event to the long term memory with a unique id.
**Arguments**:
- event (dict): The new event to be added to memory
#### search
```python
def search(query: str, k: int = 10)
```
Searches through the current memory using VectorIndexRetriever
**Arguments**:
- query (str): A query to match search results to
- k (int): Number of top results to return
**Returns**:
- List[str]: List of top k results found in current memory
@@ -1,80 +0,0 @@
---
sidebar_label: monologue
title: agenthub.monologue_agent.utils.monologue
---
## Monologue Objects
```python
class Monologue()
```
The monologue is a representation for the agent&#x27;s internal monologue where it can think.
The agent has the capability of using this monologue for whatever it wants.
#### \_\_init\_\_
```python
def __init__()
```
Initialize the empty list of thoughts
#### add\_event
```python
def add_event(t: dict)
```
Adds an event to memory if it is a valid event.
**Arguments**:
- t (dict): The thought that we want to add to memory
**Raises**:
- AgentEventTypeError: If t is not a dict
#### get\_thoughts
```python
def get_thoughts()
```
Get the current thoughts of the agent.
**Returns**:
- List: The list of thoughts that the agent has.
#### get\_total\_length
```python
def get_total_length()
```
Gives the total number of characters in all thoughts
**Returns**:
- Int: Total number of chars in thoughts.
#### condense
```python
def condense(llm: LLM)
```
Attempts to condense the monologue by using the llm
**Arguments**:
- llm (LLM): llm to be used for summarization
**Raises**:
- Exception: the same exception as it got from the llm or processing the response
@@ -1,73 +0,0 @@
---
sidebar_label: prompts
title: agenthub.monologue_agent.utils.prompts
---
#### get\_summarize\_monologue\_prompt
```python
def get_summarize_monologue_prompt(thoughts: List[dict])
```
Gets the prompt for summarizing the monologue
**Returns**:
- str: A formatted string with the current monologue within the prompt
#### get\_request\_action\_prompt
```python
def get_request_action_prompt(
task: str,
thoughts: List[dict],
background_commands_obs: List[CmdOutputObservation] = [])
```
Gets the action prompt formatted with appropriate values.
**Arguments**:
- task (str): The current task the agent is trying to accomplish
- thoughts (List[dict]): The agent&#x27;s current thoughts
- background_commands_obs (List[CmdOutputObservation]): List of all observed background commands running
**Returns**:
- str: Formatted prompt string with hint, task, monologue, and background included
#### parse\_action\_response
```python
def parse_action_response(response: str) -> Action
```
Parses a string to find an action within it
**Arguments**:
- response (str): The string to be parsed
**Returns**:
- Action: The action that was found in the response string
#### parse\_summary\_response
```python
def parse_summary_response(response: str) -> List[dict]
```
Parses a summary of the monologue
**Arguments**:
- response (str): The response string to be parsed
**Returns**:
- List[dict]: The list of summaries output by the model
@@ -1,45 +0,0 @@
---
sidebar_label: agent
title: agenthub.planner_agent.agent
---
## PlannerAgent Objects
```python
class PlannerAgent(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.
#### \_\_init\_\_
```python
def __init__(llm: LLM)
```
Initialize the Planner Agent with an LLM
**Arguments**:
- llm (LLM): The llm to be used by this agent
#### step
```python
def step(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.
**Arguments**:
- state (State): The current state given the previous actions and observations
**Returns**:
- AgentFinishAction: If the last state was &#x27;completed&#x27;, &#x27;verified&#x27;, or &#x27;abandoned&#x27;
- Action: The next action to take based on llm response
@@ -1,49 +0,0 @@
---
sidebar_label: prompt
title: agenthub.planner_agent.prompt
---
#### get\_hint
```python
def get_hint(latest_action_id: str) -> str
```
Returns action type hint based on given action_id
#### get\_prompt
```python
def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str
```
Gets the prompt for the planner agent.
Formatted with the most recent action-observation pairs, current task, and hint based on last action
**Arguments**:
- plan (Plan): The original plan outlined by the user with LLM defined tasks
- history (List[Tuple[Action, Observation]]): List of corresponding action-observation pairs
**Returns**:
- str: The formatted string prompt with historical values
#### parse\_response
```python
def parse_response(response: str) -> Action
```
Parses the model output to find a valid action to take
**Arguments**:
- response (str): A response from the model that potentially contains an Action.
**Returns**:
- Action: A valid next action to perform from model output
@@ -1,9 +0,0 @@
---
sidebar_label: action
title: opendevin.action
---
#### ACTION\_TYPE\_TO\_CLASS
type: ignore[attr-defined]
@@ -1,14 +0,0 @@
---
sidebar_label: base
title: opendevin.action.base
---
## NullAction Objects
```python
@dataclass
class NullAction(NotExecutableAction)
```
An action that does nothing.
@@ -1,16 +0,0 @@
---
sidebar_label: fileop
title: opendevin.action.fileop
---
## FileReadAction Objects
```python
@dataclass
class FileReadAction(ExecutableAction)
```
Reads a file from a given path.
Can be set to read specific lines using start and end
Default lines 0:-1 (whole file)
@@ -1,46 +0,0 @@
---
sidebar_label: github
title: opendevin.action.github
---
## GitHubPushAction Objects
```python
@dataclass
class GitHubPushAction(ExecutableAction)
```
This pushes the current branch to github.
To use this, you need to set the GITHUB_TOKEN environment variable.
The agent will return a message with a URL that you can click to make a pull
request.
**Attributes**:
- `owner` - The owner of the source repo
- `repo` - The name of the source repo
- `branch` - The branch to push
- `action` - The action identifier
## GitHubSendPRAction Objects
```python
@dataclass
class GitHubSendPRAction(ExecutableAction)
```
An action to send a github PR.
To use this, you need to set the GITHUB_TOKEN environment variable.
**Attributes**:
- `owner` - The owner of the source repo
- `repo` - The name of the source repo
- `title` - The title of the PR
- `head` - The branch to send the PR from
- `head_repo` - The repo to send the PR from
- `base` - The branch to send the PR to
- `body` - The body of the PR
@@ -1,14 +0,0 @@
---
sidebar_label: tasks
title: opendevin.action.tasks
---
## TaskStateChangedAction Objects
```python
@dataclass
class TaskStateChangedAction(NotExecutableAction)
```
Fake action, just to notify the client that a task state has changed.
-121
View File
@@ -1,121 +0,0 @@
---
sidebar_label: agent
title: opendevin.agent
---
## Agent Objects
```python
class Agent(ABC)
```
This abstract base class is an general interface for an agent dedicated to
executing a specific instruction and allowing human interaction with the
agent during execution.
It tracks the execution status and maintains a history of interactions.
#### complete
```python
@property
def complete() -> bool
```
Indicates whether the current instruction execution is complete.
**Returns**:
- complete (bool): True if execution is complete; False otherwise.
#### step
```python
@abstractmethod
def step(state: 'State') -> 'Action'
```
Starts the execution of the assigned instruction. This method should
be implemented by subclasses to define the specific execution logic.
#### search\_memory
```python
@abstractmethod
def search_memory(query: str) -> List[str]
```
Searches the agent&#x27;s memory for information relevant to the given query.
**Arguments**:
- query (str): The query to search for in the agent&#x27;s memory.
**Returns**:
- response (str): The response to the query.
#### reset
```python
def reset() -> None
```
Resets the agent&#x27;s execution status and clears the history. This method can be used
to prepare the agent for restarting the instruction or cleaning up before destruction.
#### register
```python
@classmethod
def register(cls, name: str, agent_cls: Type['Agent'])
```
Registers an agent class in the registry.
**Arguments**:
- name (str): The name to register the class under.
- agent_cls (Type[&#x27;Agent&#x27;]): The class to register.
**Raises**:
- AgentAlreadyRegisteredError: If name already registered
#### get\_cls
```python
@classmethod
def get_cls(cls, name: str) -> Type['Agent']
```
Retrieves an agent class from the registry.
**Arguments**:
- name (str): The name of the class to retrieve
**Returns**:
- agent_cls (Type[&#x27;Agent&#x27;]): The class registered under the specified name.
**Raises**:
- AgentNotRegisteredError: If name not registered
#### list\_agents
```python
@classmethod
def list_agents(cls) -> list[str]
```
Retrieves the list of all agent names from the registry.
**Raises**:
- AgentNotRegisteredError: If no agent is registered
-13
View File
@@ -1,13 +0,0 @@
---
sidebar_label: config
title: opendevin.config
---
#### get
```python
def get(key: ConfigType, required: bool = False)
```
Get a key from the environment variables or config.toml or default configs.
@@ -1,36 +0,0 @@
---
sidebar_label: agent_controller
title: opendevin.controller.agent_controller
---
## AgentController Objects
```python
class AgentController()
```
#### setup\_task
```python
async def setup_task(task: str, inputs: dict = {})
```
Sets up the agent controller with a task.
#### start
```python
async def start(task: str)
```
Starts the agent controller with a task.
If task already run before, it will continue from the last step.
#### get\_task\_state
```python
def get_task_state()
```
Returns the current state of the agent task.
-40
View File
@@ -1,40 +0,0 @@
---
sidebar_label: files
title: opendevin.files
---
## WorkspaceFile Objects
```python
class WorkspaceFile()
```
#### to\_dict
```python
def to_dict() -> Dict[str, Any]
```
Converts the File object to a dictionary.
**Returns**:
The dictionary representation of the File object.
#### get\_folder\_structure
```python
def get_folder_structure(workdir: Path) -> WorkspaceFile
```
Gets the folder structure of a directory.
**Arguments**:
- `workdir` - The directory path.
**Returns**:
The folder structure.
-56
View File
@@ -1,56 +0,0 @@
---
sidebar_label: llm
title: opendevin.llm.llm
---
## LLM Objects
```python
class LLM()
```
The LLM class represents a Language Model instance.
#### \_\_init\_\_
```python
def __init__(model=DEFAULT_MODEL_NAME,
api_key=DEFAULT_API_KEY,
base_url=DEFAULT_BASE_URL,
api_version=DEFAULT_API_VERSION,
num_retries=LLM_NUM_RETRIES,
retry_min_wait=LLM_RETRY_MIN_WAIT,
retry_max_wait=LLM_RETRY_MAX_WAIT,
llm_timeout=LLM_TIMEOUT,
llm_max_return_tokens=LLM_MAX_RETURN_TOKENS)
```
**Arguments**:
- `model` _str, optional_ - The name of the language model. Defaults to LLM_MODEL.
- `api_key` _str, optional_ - The API key for accessing the language model. Defaults to LLM_API_KEY.
- `base_url` _str, optional_ - The base URL for the language model API. Defaults to LLM_BASE_URL. Not necessary for OpenAI.
- `api_version` _str, optional_ - The version of the API to use. Defaults to LLM_API_VERSION. Not necessary for OpenAI.
- `num_retries` _int, optional_ - The number of retries for API calls. Defaults to LLM_NUM_RETRIES.
- `retry_min_wait` _int, optional_ - The minimum time to wait between retries in seconds. Defaults to LLM_RETRY_MIN_TIME.
- `retry_max_wait` _int, optional_ - The maximum time to wait between retries in seconds. Defaults to LLM_RETRY_MAX_TIME.
- `llm_timeout` _int, optional_ - The maximum time to wait for a response in seconds. Defaults to LLM_TIMEOUT.
- `llm_max_return_tokens` _int, optional_ - The maximum number of tokens to return. Defaults to LLM_MAX_RETURN_TOKENS.
**Attributes**:
- `model_name` _str_ - The name of the language model.
- `api_key` _str_ - The API key for accessing the language model.
- `base_url` _str_ - The base URL for the language model API.
- `api_version` _str_ - The version of the API to use.
#### completion
```python
@property
def completion()
```
Decorator for the litellm completion function.
-92
View File
@@ -1,92 +0,0 @@
---
sidebar_label: logger
title: opendevin.logger
---
#### get\_console\_handler
```python
def get_console_handler()
```
Returns a console handler for logging.
#### get\_file\_handler
```python
def get_file_handler()
```
Returns a file handler for logging.
#### log\_uncaught\_exceptions
```python
def log_uncaught_exceptions(ex_cls, ex, tb)
```
Logs uncaught exceptions along with the traceback.
**Arguments**:
- `ex_cls` _type_ - The type of the exception.
- `ex` _Exception_ - The exception instance.
- `tb` _traceback_ - The traceback object.
**Returns**:
None
## LlmFileHandler Objects
```python
class LlmFileHandler(logging.FileHandler)
```
__LLM prompt and response logging__
#### \_\_init\_\_
```python
def __init__(filename, mode='a', encoding='utf-8', delay=False)
```
Initializes an instance of LlmFileHandler.
**Arguments**:
- `filename` _str_ - The name of the log file.
- `mode` _str, optional_ - The file mode. Defaults to &#x27;a&#x27;.
- `encoding` _str, optional_ - The file encoding. Defaults to None.
- `delay` _bool, optional_ - Whether to delay file opening. Defaults to False.
#### emit
```python
def emit(record)
```
Emits a log record.
**Arguments**:
- `record` _logging.LogRecord_ - The log record to emit.
#### get\_llm\_prompt\_file\_handler
```python
def get_llm_prompt_file_handler()
```
Returns a file handler for LLM prompt logging.
#### get\_llm\_response\_file\_handler
```python
def get_llm_response_file_handler()
```
Returns a file handler for LLM response logging.
-29
View File
@@ -1,29 +0,0 @@
---
sidebar_label: main
title: opendevin.main
---
#### read\_task\_from\_file
```python
def read_task_from_file(file_path: str) -> str
```
Read task from the specified file.
#### read\_task\_from\_stdin
```python
def read_task_from_stdin() -> str
```
Read task from stdin.
#### main
```python
async def main(task_str: str = '')
```
Main coroutine to run the agent controller with task input flexibility.
@@ -1,9 +0,0 @@
---
sidebar_label: observation
title: opendevin.observation
---
#### OBSERVATION\_TYPE\_TO\_CLASS
type: ignore[attr-defined]
@@ -1,49 +0,0 @@
---
sidebar_label: base
title: opendevin.observation.base
---
## Observation Objects
```python
@dataclass
class Observation()
```
This data class represents an observation of the environment.
#### to\_dict
```python
def to_dict() -> dict
```
Converts the observation to a dictionary and adds user message.
#### to\_memory
```python
def to_memory() -> dict
```
Converts the observation to a dictionary.
#### message
```python
@property
def message() -> str
```
Returns a message describing the observation.
## NullObservation Objects
```python
@dataclass
class NullObservation(Observation)
```
This data class represents a null observation.
This is used when the produced action is NOT executable.
@@ -1,14 +0,0 @@
---
sidebar_label: browse
title: opendevin.observation.browse
---
## BrowserOutputObservation Objects
```python
@dataclass
class BrowserOutputObservation(Observation)
```
This data class represents the output of a browser.
@@ -1,15 +0,0 @@
---
sidebar_label: delegate
title: opendevin.observation.delegate
---
## AgentDelegateObservation Objects
```python
@dataclass
class AgentDelegateObservation(Observation)
```
This data class represents a delegate observation.
This is used when the produced action is NOT executable.
@@ -1,14 +0,0 @@
---
sidebar_label: error
title: opendevin.observation.error
---
## AgentErrorObservation Objects
```python
@dataclass
class AgentErrorObservation(Observation)
```
This data class represents an error encountered by the agent.
@@ -1,23 +0,0 @@
---
sidebar_label: files
title: opendevin.observation.files
---
## FileReadObservation Objects
```python
@dataclass
class FileReadObservation(Observation)
```
This data class represents the content of a file.
## FileWriteObservation Objects
```python
@dataclass
class FileWriteObservation(Observation)
```
This data class represents a file write operation
@@ -1,23 +0,0 @@
---
sidebar_label: message
title: opendevin.observation.message
---
## UserMessageObservation Objects
```python
@dataclass
class UserMessageObservation(Observation)
```
This data class represents a message sent by the user.
## AgentMessageObservation Objects
```python
@dataclass
class AgentMessageObservation(Observation)
```
This data class represents a message sent by the agent.
@@ -1,14 +0,0 @@
---
sidebar_label: recall
title: opendevin.observation.recall
---
## AgentRecallObservation Objects
```python
@dataclass
class AgentRecallObservation(Observation)
```
This data class represents a list of memories recalled by the agent.
@@ -1,23 +0,0 @@
---
sidebar_label: run
title: opendevin.observation.run
---
## CmdOutputObservation Objects
```python
@dataclass
class CmdOutputObservation(Observation)
```
This data class represents the output of a command.
## IPythonRunCellObservation Objects
```python
@dataclass
class IPythonRunCellObservation(Observation)
```
This data class represents the output of a IPythonRunCellAction.
-182
View File
@@ -1,182 +0,0 @@
---
sidebar_label: plan
title: opendevin.plan
---
## Task Objects
```python
class Task()
```
#### \_\_init\_\_
```python
def __init__(parent: 'Task | None',
goal: str,
state: str = OPEN_STATE,
subtasks: List = [])
```
Initializes a new instance of the Task class.
**Arguments**:
- `parent` - The parent task, or None if it is the root task.
- `goal` - The goal of the task.
- `state` - The initial state of the task.
- `subtasks` - A list of subtasks associated with this task.
#### to\_string
```python
def to_string(indent='')
```
Returns a string representation of the task and its subtasks.
**Arguments**:
- `indent` - The indentation string for formatting the output.
**Returns**:
A string representation of the task and its subtasks.
#### to\_dict
```python
def to_dict()
```
Returns a dictionary representation of the task.
**Returns**:
A dictionary containing the task&#x27;s attributes.
#### set\_state
```python
def set_state(state)
```
Sets the state of the task and its subtasks.
Args: state: The new state of the task.
**Raises**:
- `PlanInvalidStateError` - If the provided state is invalid.
#### get\_current\_task
```python
def get_current_task() -> 'Task | None'
```
Retrieves the current task in progress.
**Returns**:
The current task in progress, or None if no task is in progress.
## Plan Objects
```python
class Plan()
```
Represents a plan consisting of tasks.
**Attributes**:
- `main_goal` - The main goal of the plan.
- `task` - The root task of the plan.
#### \_\_init\_\_
```python
def __init__(task: str)
```
Initializes a new instance of the Plan class.
**Arguments**:
- `task` - The main goal of the plan.
#### \_\_str\_\_
```python
def __str__()
```
Returns a string representation of the plan.
**Returns**:
A string representation of the plan.
#### get\_task\_by\_id
```python
def get_task_by_id(id: str) -> Task
```
Retrieves a task by its ID.
**Arguments**:
- `id` - The ID of the task.
**Returns**:
The task with the specified ID.
**Raises**:
- `ValueError` - If the provided task ID is invalid or does not exist.
#### add\_subtask
```python
def add_subtask(parent_id: str, goal: str, subtasks: List = [])
```
Adds a subtask to a parent task.
**Arguments**:
- `parent_id` - The ID of the parent task.
- `goal` - The goal of the subtask.
- `subtasks` - A list of subtasks associated with the new subtask.
#### set\_subtask\_state
```python
def set_subtask_state(id: str, state: str)
```
Sets the state of a subtask.
**Arguments**:
- `id` - The ID of the subtask.
- `state` - The new state of the subtask.
#### get\_current\_task
```python
def get_current_task()
```
Retrieves the current task in progress.
**Returns**:
The current task in progress, or None if no task is in progress.
@@ -1,19 +0,0 @@
---
sidebar_label: sandbox
title: opendevin.sandbox.e2b.sandbox
---
## E2BBox Objects
```python
class E2BBox(Sandbox)
```
#### copy\_to
```python
def copy_to(host_src: str, sandbox_dest: str, recursive: bool = False)
```
Copies a local file or directory to the sandbox.
@@ -1,16 +0,0 @@
---
sidebar_label: jupyter
title: opendevin.sandbox.plugins.jupyter
---
## JupyterRequirement Objects
```python
@dataclass
class JupyterRequirement(PluginRequirement)
```
#### host\_src
The directory of this file (sandbox/plugins/jupyter)
@@ -1,21 +0,0 @@
---
sidebar_label: mixin
title: opendevin.sandbox.plugins.mixin
---
## PluginMixin Objects
```python
class PluginMixin()
```
Mixin for Sandbox to support plugins.
#### init\_plugins
```python
def init_plugins(requirements: List[PluginRequirement])
```
Load a plugin into the sandbox.
@@ -1,14 +0,0 @@
---
sidebar_label: requirement
title: opendevin.sandbox.plugins.requirement
---
## PluginRequirement Objects
```python
@dataclass
class PluginRequirement()
```
Requirement for a plugin.
@@ -1,88 +0,0 @@
---
sidebar_label: action
title: opendevin.schema.action
---
## ActionTypeSchema Objects
```python
class ActionTypeSchema(BaseModel)
```
#### INIT
Initializes the agent. Only sent by client.
#### USER\_MESSAGE
Sends a message from the user. Only sent by the client.
#### START
Starts a new development task OR send chat from the user. Only sent by the client.
#### READ
Reads the content of a file.
#### WRITE
Writes the content to a file.
#### RUN
Runs a command.
#### RUN\_IPYTHON
Runs a IPython cell.
#### KILL
Kills a background command.
#### BROWSE
Opens a web page.
#### RECALL
Searches long-term memory
#### THINK
Allows the agent to make a plan, set a goal, or record thoughts
#### TALK
Allows the agent to respond to the user.
#### DELEGATE
Delegates a task to another agent.
#### FINISH
If you&#x27;re absolutely certain that you&#x27;ve completed your task and have tested your work,
use the finish action to stop working.
#### PAUSE
Pauses the task.
#### RESUME
Resumes the task.
#### STOP
Stops the task. Must send a start action to restart a new task.
#### PUSH
Push a branch to github.
#### SEND\_PR
Send a PR to github.
@@ -1,39 +0,0 @@
---
sidebar_label: observation
title: opendevin.schema.observation
---
## ObservationTypeSchema Objects
```python
class ObservationTypeSchema(BaseModel)
```
#### READ
The content of a file
#### BROWSE
The HTML content of a URL
#### RUN
The output of a command
#### RUN\_IPYTHON
Runs a IPython cell.
#### RECALL
The result of a search
#### CHAT
A message from the user
#### DELEGATE
The result of a task delegated to another agent
@@ -1,61 +0,0 @@
---
sidebar_label: task
title: opendevin.schema.task
---
## TaskState Objects
```python
class TaskState(str, Enum)
```
#### INIT
Initial state of the task.
#### RUNNING
The task is running.
#### AWAITING\_USER\_INPUT
The task is awaiting user input.
#### PAUSED
The task is paused.
#### STOPPED
The task is stopped.
#### FINISHED
The task is finished.
#### ERROR
An error occurred during the task.
## TaskStateAction Objects
```python
class TaskStateAction(str, Enum)
```
#### START
Starts the task.
#### PAUSE
Pauses the task.
#### RESUME
Resumes the task.
#### STOP
Stops the task.
@@ -1,132 +0,0 @@
---
sidebar_label: agent
title: opendevin.server.agent.agent
---
## AgentUnit Objects
```python
class AgentUnit()
```
Represents a session with an agent.
**Attributes**:
- `controller` - The AgentController instance for controlling the agent.
- `agent_task` - The task representing the agent&#x27;s execution.
#### \_\_init\_\_
```python
def __init__(sid)
```
Initializes a new instance of the Session class.
#### send\_error
```python
async def send_error(message)
```
Sends an error message to the client.
**Arguments**:
- `message` - The error message to send.
#### send\_message
```python
async def send_message(message)
```
Sends a message to the client.
**Arguments**:
- `message` - The message to send.
#### send
```python
async def send(data)
```
Sends data to the client.
**Arguments**:
- `data` - The data to send.
#### dispatch
```python
async def dispatch(action: str | None, data: dict)
```
Dispatches actions to the agent from the client.
#### get\_arg\_or\_default
```python
def get_arg_or_default(_args: dict, key: ConfigType) -> str
```
Gets an argument from the args dictionary or the default value.
**Arguments**:
- `_args` - The args dictionary.
- `key` - The key to get.
**Returns**:
The value of the key or the default value.
#### create\_controller
```python
async def create_controller(start_event: dict)
```
Creates an AgentController instance.
**Arguments**:
- `start_event` - The start event data (optional).
#### start\_task
```python
async def start_task(start_event)
```
Starts a task for the agent.
**Arguments**:
- `start_event` - The start event data.
#### set\_task\_state
```python
async def set_task_state(new_state_action: TaskStateAction)
```
Sets the state of the agent task.
#### on\_agent\_event
```python
async def on_agent_event(event: Observation | Action)
```
Callback function for agent events.
**Arguments**:
- `event` - The agent event (Observation or Action).
@@ -1,31 +0,0 @@
---
sidebar_label: manager
title: opendevin.server.agent.manager
---
## AgentManager Objects
```python
class AgentManager()
```
#### register\_agent
```python
def register_agent(sid: str)
```
Registers a new agent.
**Arguments**:
- `sid` - The session ID of the agent.
#### dispatch
```python
async def dispatch(sid: str, action: str | None, data: dict)
```
Dispatches actions to the agent from the client.
@@ -1,30 +0,0 @@
---
sidebar_label: auth
title: opendevin.server.auth.auth
---
#### get\_sid\_from\_token
```python
def get_sid_from_token(token: str) -> str
```
Retrieves the session id from a JWT token.
**Arguments**:
- `token` _str_ - The JWT token from which the session id is to be extracted.
**Returns**:
- `str` - The session id if found and valid, otherwise an empty string.
#### sign\_token
```python
def sign_token(payload: Dict[str, object]) -> str
```
Signs a JWT token.
@@ -1,34 +0,0 @@
---
sidebar_label: listen
title: opendevin.server.listen
---
#### get\_litellm\_models
```python
@app.get('/api/litellm-models')
async def get_litellm_models()
```
Get all models supported by LiteLLM.
#### get\_agents
```python
@app.get('/api/agents')
async def get_agents()
```
Get all agents supported by LiteLLM.
#### get\_token
```python
@app.get('/api/auth')
async def get_token(
credentials: HTTPAuthorizationCredentials = Depends(security_scheme))
```
Generate a JWT for authentication when starting a WebSocket connection. This endpoint checks if valid credentials
are provided and uses them to get a session ID. If no valid credentials are provided, it generates a new session ID.
@@ -1,35 +0,0 @@
---
sidebar_label: manager
title: opendevin.server.session.manager
---
## SessionManager Objects
```python
class SessionManager()
```
#### send
```python
async def send(sid: str, data: Dict[str, object]) -> bool
```
Sends data to the client.
#### send\_error
```python
async def send_error(sid: str, message: str) -> bool
```
Sends an error message to the client.
#### send\_message
```python
async def send_message(sid: str, message: str) -> bool
```
Sends a message to the client.
@@ -1,15 +0,0 @@
---
sidebar_label: msg_stack
title: opendevin.server.session.msg_stack
---
## Message Objects
```python
class Message()
```
#### role
&quot;user&quot;| &quot;assistant&quot;
@@ -1,27 +0,0 @@
---
sidebar_label: session
title: opendevin.server.session.session
---
## Session Objects
```python
class Session()
```
#### send\_error
```python
async def send_error(message: str) -> bool
```
Sends an error message to the client.
#### send\_message
```python
async def send_message(message: str) -> bool
```
Sends a message to the client.
@@ -1,13 +0,0 @@
---
sidebar_label: system
title: opendevin.utils.system
---
#### find\_available\_tcp\_port
```python
def find_available_tcp_port() -> int
```
Find an available TCP port, return -1 if none available.
+3
View File
@@ -0,0 +1,3 @@
# Python Docs
Docs will appear here after deployment.
+2 -204
View File
@@ -1,207 +1,5 @@
{
"items": [
{
"items": [
{
"items": [
"python/agenthub/SWE_agent/agent",
"python/agenthub/SWE_agent/parser"
],
"label": "agenthub.SWE_agent",
"type": "category"
},
{
"items": [
"python/agenthub/codeact_agent/codeact_agent"
],
"label": "agenthub.codeact_agent",
"type": "category"
},
{
"items": [
"python/agenthub/delegator_agent/agent"
],
"label": "agenthub.delegator_agent",
"type": "category"
},
{
"items": [
"python/agenthub/dummy_agent/agent"
],
"label": "agenthub.dummy_agent",
"type": "category"
},
{
"items": [
"python/agenthub/micro/agent"
],
"label": "agenthub.micro",
"type": "category"
},
{
"items": [
{
"items": [
"python/agenthub/monologue_agent/utils/json",
"python/agenthub/monologue_agent/utils/memory",
"python/agenthub/monologue_agent/utils/monologue",
"python/agenthub/monologue_agent/utils/prompts"
],
"label": "agenthub.monologue_agent.utils",
"type": "category"
},
"python/agenthub/monologue_agent/agent"
],
"label": "agenthub.monologue_agent",
"type": "category"
},
{
"items": [
"python/agenthub/planner_agent/agent",
"python/agenthub/planner_agent/prompt"
],
"label": "agenthub.planner_agent",
"type": "category"
}
],
"label": "agenthub",
"type": "category"
},
{
"items": [
{
"items": [
"python/opendevin/action/__init__",
"python/opendevin/action/base",
"python/opendevin/action/fileop",
"python/opendevin/action/github",
"python/opendevin/action/tasks"
],
"label": "opendevin.action",
"type": "category"
},
{
"items": [
"python/opendevin/controller/agent_controller"
],
"label": "opendevin.controller",
"type": "category"
},
{
"items": [
"python/opendevin/llm/llm"
],
"label": "opendevin.llm",
"type": "category"
},
{
"items": [
"python/opendevin/observation/__init__",
"python/opendevin/observation/base",
"python/opendevin/observation/browse",
"python/opendevin/observation/delegate",
"python/opendevin/observation/error",
"python/opendevin/observation/files",
"python/opendevin/observation/message",
"python/opendevin/observation/recall",
"python/opendevin/observation/run"
],
"label": "opendevin.observation",
"type": "category"
},
{
"items": [
{
"items": [
"python/opendevin/sandbox/docker/process"
],
"label": "opendevin.sandbox.docker",
"type": "category"
},
{
"items": [
"python/opendevin/sandbox/e2b/sandbox"
],
"label": "opendevin.sandbox.e2b",
"type": "category"
},
{
"items": [
{
"items": [
"python/opendevin/sandbox/plugins/jupyter/__init__"
],
"label": "opendevin.sandbox.plugins.jupyter",
"type": "category"
},
"python/opendevin/sandbox/plugins/mixin",
"python/opendevin/sandbox/plugins/requirement"
],
"label": "opendevin.sandbox.plugins",
"type": "category"
}
],
"label": "opendevin.sandbox",
"type": "category"
},
{
"items": [
"python/opendevin/schema/action",
"python/opendevin/schema/observation",
"python/opendevin/schema/task"
],
"label": "opendevin.schema",
"type": "category"
},
{
"items": [
{
"items": [
"python/opendevin/server/agent/agent",
"python/opendevin/server/agent/manager"
],
"label": "opendevin.server.agent",
"type": "category"
},
{
"items": [
"python/opendevin/server/auth/auth"
],
"label": "opendevin.server.auth",
"type": "category"
},
{
"items": [
"python/opendevin/server/session/manager",
"python/opendevin/server/session/msg_stack",
"python/opendevin/server/session/session"
],
"label": "opendevin.server.session",
"type": "category"
},
"python/opendevin/server/listen"
],
"label": "opendevin.server",
"type": "category"
},
{
"items": [
"python/opendevin/utils/system"
],
"label": "opendevin.utils",
"type": "category"
},
"python/opendevin/agent",
"python/opendevin/config",
"python/opendevin/files",
"python/opendevin/logger",
"python/opendevin/main",
"python/opendevin/plan"
],
"label": "opendevin",
"type": "category"
}
],
"items": ["python/python"],
"label": "Backend",
"type": "category"
}
}

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