Compare commits

..

210 Commits
0.3.0 ... 0.5.0

Author SHA1 Message Date
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
Tom Mery
545327cc1e upload file to workspace with button (#1394)
* upload file to workspace with button

* resolve filepath

* regenerate lock file

* fix lock file

* fix torch version

* fix lock

* fix poerty.lock

* fix lint

---------

Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
2024-05-01 15:40:35 +00:00
Boxuan Li
c7dd443fa2 CoderAgent: Render summary prompt conditionally (#1461)
* CoderAgent: Render repo summary conditionally

* Add unittests

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-05-01 15:40:20 +00:00
Robert Brennan
c50319138e Revert "feat(makefile): add capability to skip Docker image pull (#1463)" (#1489)
This reverts commit 442ab7371c.
2024-05-01 11:00:06 -04:00
Xingyao Wang
d80f025e21 Implement Jupyter Frontend (#1363)
* initialize plugin definition

* initialize plugin definition

* simplify mixin

* further improve plugin mixin

* add cache dir for pip

* support clean up cache

* add script for setup jupyter and execution server

* integrate JupyterRequirement to ssh_box

* source bashrc at the end of plugin load

* add execute_cli that accept code via stdin

* make JUPYTER_EXEC_SERVER_PORT configurable via env var

* increase background cmd sleep time

* Update opendevin/sandbox/plugins/mixin.py

Co-authored-by: Robert Brennan <accounts@rbren.io>

* add mixin to base class

* make jupyter requirement a dataclass

* source plugins only when >0 requirements

* add `sandbox_plugins` for each agent & have controller take care of it

* update build.sh to make logs available in /opendevin/logs

* switch to use config for lib and cache dir

* Add SANDBOX_WORKSPACE_DIR into config

* Add SANDBOX_WORKSPACE_DIR into config

* fix occurence of /workspace

* fix permission issue with /workspace

* use python to implement execute_cli to avoid stdin escape issue

* add IPythonRunCellAction and get it working

* wait until jupyter is avaialble

* support plugin via copying instead of mounting

* add agent talk action

* support follow-up user language feedback

* add __str__ for action to be printed better

* only print PLAN at the beginning

* wip: update codeact agent

* get rid the initial messate

* update codeact agent to handle null action;
add thought to bash

* dispatch thought for RUN action as well

* fix weird behavior of pxssh where the output would not flush correctly

* make ssh box can handle exit_code properly as well

* add initial version of swe-agent plugin;

* rename swe cursors

* split setup script into two and create two requirements

* print SWE-agent command documentation

* update swe-agent to default to no custom docs

* add initial version of swe-agent plugin;

* rename swe cursors

* split setup script into two and create two requirements

* print SWE-agent command documentation

* update swe-agent to default to no custom docs

* update dockerfile with dependency from swe-agent

* make env setup a separate script for .bashrc source

* add wip prompt

* fix mount_dir for ssh_box

* update prompt

* fix mount_dir for ssh_box

* default to use host network

* default to use host network

* move prompt to a separate file

* fix swe-tool plugins;
add missing _split_string

* remove hostname from sshbox

* update the prompt with edit functionality

* fix swe-tool plugins;
add missing _split_string

* add awaiting into status bar

* fix the bug of additional send event

* remove some print action

* move logic to config.py

* remove debugging comments

* make host network as default

* make WORKSPACE_MOUNT_PATH as abspath

* implement execute_cli via file cp

* Revert "implement execute_cli via file cp"

This reverts commit 06f0155bc1.

* add codeact dependencies to default container

* add IPythonRunCellObservation

* add back cache dir and default to /tmp

* make USE_HOST_NETWORK a bool

* revert use host network to false

* add temporarily fix for IPython RUN action

* preliminary implementation of CodeActAgent's jupyter

* update node module

* update prompt

* revert USE_HOST_NETWORK to true since it is not affecting anything

* attempt to fix lint

* remove newline

* update prompt

* Refactor browser style. (#1358)

* delete useless assets and css class.
* add waiting for page loaded (networkidle with 3s timeout)

* Add integration test framework with mock llm (#1301)

* Add integration test framework with mock llm

* Fix MonologueAgent and PlannerAgent tests

* Remove adhoc logging

* Use existing logs

* Fix SWEAgent and PlannerAgent

* Check-in test log files

* conftest: look up under test name folder only

* Add docstring to conftest

* Finish dev doc

* Avoid non-determinism

* Remove dependency on llm embedding model

* Init embedding model only for MonologueAgent

* Add adhoc fix for sandbox discrepancy

* Test ssh and exec sandboxes

* CI: fix missing sandbox type

* conftest: Remove hack

* Reword comment for TODO

* Revert "refactor(frontend): Terminal (#1315)" (#1360)

This reverts commit 27246aca7e.

* revert USE_HOST_NETWORK to true since it is not affecting anything

* attempt to fix lint

* handle IsADirectory errors (#1365)

* update to 0.4.0 (#1362)

Co-authored-by: Jim Su <jimsu@protonmail.com>

* feat(frontend): multiple design changes (#1370)

* fix/improve terminal hook (#1371)

* Revert "update node module"

This reverts commit 459b1031e7.

* support SyntaxHighlighter and markdown for jupyter visualization

* fix jupyter execution server

* make jupyter active

* improve the display of markdown and raw text

* get base64 image display for react

* add `thought` to most action class

* fix unit tests for current action abstraction

* support user exit

* update test cases with the latest action format (added 'thought')

* fix integration test for CodeActAGent by mocking stdin

* only mock stdin for tests with user_responses.log

* remove -exec integration test for CodeActAgent since it is not supported

* remove specific stop word

* fix comments

* improve clarity of prompt

* attempt to fix lint

* attempt to fix lint yet agiain

* fix py lint

* fix integration tests

* sandbox might failed in chown due to mounting, but it won't be fatal

* update debug instruction for sshbox

* fix typo

* get RUN_AS_DEVIN and network=host working with app sandbox

* get RUN_AS_DEVIN and network=host working with app sandbox

* attempt to fix the workspace base permission

* sandbox might failed in chown due to mounting, but it won't be fatal

* update sshbox instruction

* remove default user id since it will be passed in the instruction

* revert permission fix since it should be resolved by correct SANDBOX_USER_ID

* the permission issue can be fixed by simply provide correct env var

* remove log

* set sandbox user id to getuid by default

* move logging to initializer

* make the uid consistent across host, app container, and sandbox

* remove hostname as it causes sudo issue

* fix permission of entrypoint script

* make the uvicron app run as host user uid for jupyter plugin

* add warning message

* fix frontend lint

* update dev md for instruction of running unit tests

* add back unit tests

* revert back to the original sandbox implementation to fix testcases

* revert use host network

* get docker socket gid and usermod instead of chmod 777

* allow unit test workflow to find docker.sock

* make sandbox test working via patch

* fix arg parser that's broken for some reason

* try to fix app build disk space issue

* fix integration test

* Revert "fix arg parser that's broken for some reason"

This reverts commit 6cc8961133.

* update Development.md

* cleanup intergration tests & add exception for CodeAct+execbox

* fix config

* implement user_message action

* fix doc

* fix event dict error

* fix frontend lint

* revert accidentally changes to integration tests

* revert accidentally changes to integration tests

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Leo <ifuryst@gmail.com>
Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Alex Bäuerle <alex@a13x.io>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
Co-authored-by: Robert Brennan <contact@rbren.io>
2024-05-01 22:37:01 +08:00
Arno.Edwards
442ab7371c feat(makefile): add capability to skip Docker image pull (#1463)
* feat(makefile): add capability to skip Docker image pull

* ci(github-actions): add conditional Docker installation based on ENV variable
2024-05-01 09:01:11 -04:00
Xingyao Wang
1c7cdbefdd feat(CodeActAgent): Support Agent-User Interaction during Task Execution and the Full Integration of CodeActAgent (#1290)
* initialize plugin definition

* initialize plugin definition

* simplify mixin

* further improve plugin mixin

* add cache dir for pip

* support clean up cache

* add script for setup jupyter and execution server

* integrate JupyterRequirement to ssh_box

* source bashrc at the end of plugin load

* add execute_cli that accept code via stdin

* make JUPYTER_EXEC_SERVER_PORT configurable via env var

* increase background cmd sleep time

* Update opendevin/sandbox/plugins/mixin.py

Co-authored-by: Robert Brennan <accounts@rbren.io>

* add mixin to base class

* make jupyter requirement a dataclass

* source plugins only when >0 requirements

* add `sandbox_plugins` for each agent & have controller take care of it

* update build.sh to make logs available in /opendevin/logs

* switch to use config for lib and cache dir

* Add SANDBOX_WORKSPACE_DIR into config

* Add SANDBOX_WORKSPACE_DIR into config

* fix occurence of /workspace

* fix permission issue with /workspace

* use python to implement execute_cli to avoid stdin escape issue

* add IPythonRunCellAction and get it working

* wait until jupyter is avaialble

* support plugin via copying instead of mounting

* add agent talk action

* support follow-up user language feedback

* add __str__ for action to be printed better

* only print PLAN at the beginning

* wip: update codeact agent

* get rid the initial messate

* update codeact agent to handle null action;
add thought to bash

* dispatch thought for RUN action as well

* fix weird behavior of pxssh where the output would not flush correctly

* make ssh box can handle exit_code properly as well

* add initial version of swe-agent plugin;

* rename swe cursors

* split setup script into two and create two requirements

* print SWE-agent command documentation

* update swe-agent to default to no custom docs

* add initial version of swe-agent plugin;

* rename swe cursors

* split setup script into two and create two requirements

* print SWE-agent command documentation

* update swe-agent to default to no custom docs

* update dockerfile with dependency from swe-agent

* make env setup a separate script for .bashrc source

* add wip prompt

* fix mount_dir for ssh_box

* update prompt

* fix mount_dir for ssh_box

* default to use host network

* default to use host network

* move prompt to a separate file

* fix swe-tool plugins;
add missing _split_string

* remove hostname from sshbox

* update the prompt with edit functionality

* fix swe-tool plugins;
add missing _split_string

* add awaiting into status bar

* fix the bug of additional send event

* remove some print action

* move logic to config.py

* remove debugging comments

* make host network as default

* make WORKSPACE_MOUNT_PATH as abspath

* implement execute_cli via file cp

* Revert "implement execute_cli via file cp"

This reverts commit 06f0155bc1.

* add codeact dependencies to default container

* add IPythonRunCellObservation

* add back cache dir and default to /tmp

* make USE_HOST_NETWORK a bool

* revert use host network to false

* add temporarily fix for IPython RUN action

* update prompt

* revert USE_HOST_NETWORK to true since it is not affecting anything

* attempt to fix lint

* remove newline

* fix jupyter execution server

* add `thought` to most action class

* fix unit tests for current action abstraction

* support user exit

* update test cases with the latest action format (added 'thought')

* fix integration test for CodeActAGent by mocking stdin

* only mock stdin for tests with user_responses.log

* remove -exec integration test for CodeActAgent since it is not supported

* remove specific stop word

* fix comments

* improve clarity of prompt

* fix py lint

* fix integration tests

* sandbox might failed in chown due to mounting, but it won't be fatal

* update debug instruction for sshbox

* fix typo

* get RUN_AS_DEVIN and network=host working with app sandbox

* get RUN_AS_DEVIN and network=host working with app sandbox

* attempt to fix the workspace base permission

* sandbox might failed in chown due to mounting, but it won't be fatal

* update sshbox instruction

* remove default user id since it will be passed in the instruction

* revert permission fix since it should be resolved by correct SANDBOX_USER_ID

* the permission issue can be fixed by simply provide correct env var

* remove log

* set sandbox user id to getuid by default

* move logging to initializer

* make the uid consistent across host, app container, and sandbox

* remove hostname as it causes sudo issue

* fix permission of entrypoint script

* make the uvicron app run as host user uid for jupyter plugin

* add warning message

* update dev md for instruction of running unit tests

* add back unit tests

* revert back to the original sandbox implementation to fix testcases

* revert use host network

* get docker socket gid and usermod instead of chmod 777

* allow unit test workflow to find docker.sock

* make sandbox test working via patch

* fix arg parser that's broken for some reason

* try to fix app build disk space issue

* fix integration test

* Revert "fix arg parser that's broken for some reason"

This reverts commit 6cc8961133.

* update Development.md

* cleanup intergration tests & add exception for CodeAct+execbox

* fix config

* implement user_message action

* fix doc

* fix event dict error

* fix frontend lint

* revert accidentally changes to integration tests

* revert accidentally changes to integration tests

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Robert Brennan <contact@rbren.io>
2024-05-01 08:40:00 -04:00
Engel Nyst
ea214d1c07 Re-initialize monologue agent after reset (#1478)
* re-initialize monologue agent after reset

* Add to docstring

* Remove now duplicated instances
2024-05-01 08:38:11 -04:00
amodev
62e4fb47b2 Fix API key exposure in toast notifications, resolves #1477 (#1480) 2024-05-01 06:01:48 +00:00
sp.wack
5f2d6f58f2 update tests after setting modals upgrade (#1467) 2024-04-30 10:18:32 -07:00
Robert Brennan
0cda5f64af Add integration test with dummy agent (#1316)
* first pass at dummy

* add assertion to dummy

* add dummy workflow

* beef up tests

* try and fix huggingface issue

* remove newlines

* rename test

* move to pytest

* Revert " move to pytest"

This reverts commit de8121c400.

* fix lint

* delint

* Update .github/workflows/dummy-agent-test.yml

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

---------

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
2024-04-30 16:52:00 +00:00
ChengyangDu
eb1c3d8790 fix: configurable sand box user id (#1459) 2024-04-30 16:51:40 +00:00
Xingyao Wang
fe43aeb9b6 Revert ssh box implemetation, fix multi-line command issues and add unit tests (#1460)
* update dev md for instruction of running unit tests

* add back unit tests

* revert back to the original sandbox implementation to fix testcases

* allow unit test workflow to find docker.sock

* make sandbox test working via patch

* fix arg parser that's broken for some reason

* fix integration test

* Revert "fix arg parser that's broken for some reason"

This reverts commit 6cc8961133.

* update Development.md
2024-04-30 12:46:35 -04:00
Xingyao Wang
ccbbabac1c Get RUN_AS_DEVIN working with app sandbox (#1426)
* get RUN_AS_DEVIN and network=host working with app sandbox

* attempt to fix the workspace base permission

* sandbox might failed in chown due to mounting, but it won't be fatal

* update sshbox instruction

* remove default user id since it will be passed in the instruction

* revert permission fix since it should be resolved by correct SANDBOX_USER_ID

* the permission issue can be fixed by simply provide correct env var

* remove log

* set sandbox user id to getuid by default

* move logging to initializer

* make the uid consistent across host, app container, and sandbox

* remove hostname as it causes sudo issue

* fix permission of entrypoint script

* make the uvicron app run as host user uid for jupyter plugin

* revert use host network

* get docker socket gid and usermod instead of chmod 777

* try to fix app build disk space issue
2024-04-30 12:39:52 -04:00
Alex Bäuerle
eb7703a44e feat(frontend): add empty states to all tabs according to Figma (#1456)
* feat(frontend): add empty states to all tabs according to Figma

* import order

---------

Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2024-04-30 12:34:23 -04:00
Robert Brennan
31c1a2d748 Update bug_template.yml (#1458) 2024-04-30 03:36:39 +02:00
Aleksandar
5989853f7a Fix duplicate LLM_BASE_URL entry in config.toml and enable different ollama embeddings (#1437)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-30 02:26:02 +02:00
Xingyao Wang
fe8bb51f17 Update debug instruction for SSH Box (#1425)
* update debug instruction for sshbox

* fix typo

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-29 20:09:56 -04:00
Jirka Borovec
0c2ebfd6e1 Ruff: use I rule for isort (#1410)
Ruff: use I rule for isort
2024-04-29 15:41:58 -07:00
Xia Zhenhua
086a2ed17f feat: make the response of agent_controller better to process when exception in step execution (#1445)
* feat: make the response of agent_controller better to process when exception occurred during executing step.

* Update opendevin/controller/agent_controller.py

---------

Co-authored-by: aaren.xzh <aaren.xzh@antfin.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-29 21:21:26 +00:00
Alex Bäuerle
fa067edbac feat: set API key from settings modal (#1319)
* feat: set API key from settings modal

* feat: init with api key

* test

* fix

* fixes

* fix api key reference

* test

* minor fixes

* fix settings update

* combine settings call

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2024-04-29 23:18:00 +03:00
Robert Brennan
11d48cc2f3 Run docker on forked pull requests (#1450)
* build docker on pull request

* run docker build on PRs

* remove if

* add permissions

* change ghcr login

* empty commit

* always use opendevin as org

* lowercase

* no client token

* dont push on forks

* remove env

* only cache-to if pushing

* fix org name

* fix owner

* Update containers/build.sh

Co-authored-by: Graham Neubig <neubig@gmail.com>

* lowercase

* remove tag prefix

* lowercase

---------

Co-authored-by: Graham Neubig <neubig@gmail.com>
2024-04-29 14:24:28 -04:00
Graham Neubig
c821d0967c Point docker errors to troubleshooting doc (#1453) 2024-04-29 18:21:55 +00:00
Alex Bäuerle
b47fdbb23b docs(readme): remove demo video (#1455)
The demo video is included in our homepage now. If we're linking to a video file local to the repository from the README, it does not get displayed. Instead, there is just a link to it. I don't think it's a good idea to always have to update both the readme video and the website video, so I am removing the readme video.
2024-04-29 11:09:48 -07:00
Alex Bäuerle
b3930ad195 ci: fix deploy config (#1454) 2024-04-29 17:28:30 +00:00
Alex Bäuerle
cd58194d2a docs(docs): start implementing docs website (#1372)
* docs(docs): start implementing docs website

* update video url

* add autogenerated codebase docs for backend

* precommit

* update links

* fix config and video

* gh actions

* rename

* workdirs

* path

* path

* fix doc1

* redo markdown

* docs

* change main folder name

* simplify readme

* add back architecture

* Fix lint errors

* lint

* update poetry lock

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-29 10:00:51 -07:00
Jirka Borovec
46bd83678a precommit: add pyproject's hooks (#1412)
* precommit: add `pyproject`'s hooks

* apply precommit

* Update dev_config/python/.pre-commit-config.yaml

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-29 14:58:21 +00:00
sp.wack
1597f0093e fix custom llm model value not displaying (#1447) 2024-04-29 10:46:39 -04:00
sp.wack
c02b681728 update chat input tests to expect new behaviour (#1443) 2024-04-29 10:37:33 -04:00
Boxuan Li
319b9ac0f3 Fix micro-agents schema bug (#1424)
* Fix micro agents definitions

* Add tests for micro agents

* Add to CI

* Revert "Add to CI"

This reverts commit 94f3b4e7c8.

* Remove test artifacts for ManagerAgent

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-29 10:34:03 -04:00
Jirka Borovec
5a9b6b0c9f precommit: remove unused setup-cfg-fmt (#1411) 2024-04-29 10:33:50 -04:00
Boxuan Li
a5a1545c59 Add Codecov test coverage (#1397)
* Add pytest-cov dependency

* Add codecov to unit test CI

* Codecov: Use secrets

* Add codecov for integration tests
2024-04-29 10:33:13 -04:00
Arno.Edwards
03ecec1e76 ci: refine job matrix and enable cache for poetry (#1381)
* ci: refine job matrix and enable cache for poetry

- Replace direct installation of Poetry with pipx to ensure isolated environment setups.
- Enable caching for Poetry dependencies using setup-python action to improve build efficiency.
- Refactor the job matrix specifications.

* ci: enable Homebrew caching in Actions

* ci: optimize Docker and Colima installation in GitHub Actions

- Check if Docker and Colima are already installed before attempting to install them. Link and start each service appropriately to avoid unnecessary reinstallation and ensure they are ready for immediate use in the CI pipeline.

* ci: remove Homebrew cache.

* fix typo

* fix: specified the Python version to avoid errors in actions.

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-29 10:32:25 -04:00
sp.wack
8954855ba3 refactor(frontend): settings and friends (#1400)
* refactor settings and friends

* extend base modal to support disabled property

* extend settings handler to change language via i18next

* remove unused settings.d.ts

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-28 21:59:26 -04:00
Christian Balcom
24b71927c3 fix(backend) changes to improve Command-R+ behavior, plus file i/o error improvements, attempt 2 (#1417)
* Some improvements to prompts, some better exception handling for various file IO errors, added timeout and max return token configurations for the LLM api.

* More monologue prompt improvements

* Dynamically set username provided in prompt.

* Remove absolute paths from llm prompts, fetch working directory from sandbox when resolving paths in fileio operations, add customizable timeout for bash commands, mention said timeout in llm prompt.

* Switched ssh_box to disabling tty echo and removed the logic attempting to delete it from the response afterwards, fixed get_working_directory for ssh_box.

* Update prompts in integration tests to match monologue agent changes.

* Minor tweaks to make merge easier.

* Another minor prompt tweak, better invalid json handling.

* Fix lint error

* More catch-up to fix lint errors introduced by merge.

* Force WORKSPACE_MOUNT_PATH_IN_SANDBOX to match WORKSPACE_MOUNT_PATH in local sandbox mode, combine exception handlers in prompts.py.

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-28 21:58:53 -04:00
Graham Neubig
a5f61caae9 Add actions for github push and send PR (#1415)
* Added a push action

* Tests

* Add tests

* Fix capitalization

* Update

* Fix typo

* Fix integration tests

* Added poetry.lock

* Set lock

* Fix action parsing

* Update integration test output

* Updated prompt

* Update integration test

* Add github token to default config

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-29 00:56:23 +00:00
Graham Neubig
0d224de369 Fixed typo in integration tests README.md (#1438)
* Fixed typo in integration tests README.md

* Update tests/integration/README.md

---------

Co-authored-by: ThoughtfulRobot <robot@example.com>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-28 20:44:58 -04:00
Graham Neubig
567e2c2b35 Add poetry install to makefile (#1436) 2024-04-28 22:40:37 +01:00
Xingyao Wang
a7e42ff0a3 allow interaction with local box (#1429) 2024-04-28 23:24:01 +08:00
Robert Brennan
d1f62bb6be Add final step for matrix tests, some cleanup (#1408)
* add final step for matrix tests

* change test names

* add names

* change run conditions

* fix test names
2024-04-27 14:34:07 -04:00
Robert Brennan
9c9aee29f0 Revert "fix(backend) changes to improve Command-R+ behavior, plus file i/o er…" (#1405)
This reverts commit 44aea95dde.
2024-04-27 08:57:04 -04:00
sp.wack
d8386366d9 feature(frontend): improve chat overflow handling and syntax highlighting (#1402)
* improve overflow handling and syntax highlighting

* add reference to gh page
2024-04-27 08:21:29 -04:00
Engel Nyst
ca73ecb499 message in setup-config about model (#1398) 2024-04-27 08:19:31 -04:00
Leo
6f90239521 Fix: enable input but don't be allowed to submit when agent init (#1353)
* Fix: enable input but don't be allowed to submit when agent initializing.

* Prevent Enter from adding a newline when disabled.

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-27 12:17:58 +00:00
Christian Balcom
546be7ca8e Handle Unicode errors (#1388)
* Add errors for non-unicode file data or command return, trim excessively long command returns.

* Fix lint issue.

* Fix lint issue (try 2).

* Realized that prompts were trimmed elsewhere and dropped the new addition.
2024-04-27 08:15:16 -04:00
Robert Brennan
ce47461c5a disable host network (#1404) 2024-04-27 08:07:25 -04:00
Christian Balcom
44aea95dde fix(backend) changes to improve Command-R+ behavior, plus file i/o error improvements. (#1347)
* Some improvements to prompts, some better exception handling for various file IO errors, added timeout and max return token configurations for the LLM api.

* More monologue prompt improvements

* Dynamically set username provided in prompt.

* Remove absolute paths from llm prompts, fetch working directory from sandbox when resolving paths in fileio operations, add customizable timeout for bash commands, mention said timeout in llm prompt.

* Switched ssh_box to disabling tty echo and removed the logic attempting to delete it from the response afterwards, fixed get_working_directory for ssh_box.

* Update prompts in integration tests to match monologue agent changes.

* Minor tweaks to make merge easier.

* Another minor prompt tweak, better invalid json handling.

* Fix lint error

* More catch-up to fix lint errors introduced by merge.

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-27 11:58:34 +00:00
Jirka Borovec
e32d95cb1a lint: simplify hooks already covered by Ruff (#1204)
* lint: simplify hooks already covered by Ruff

* prune dev dependency

* setting E, W, F

* poetry?

* autopep8

* quote-style = "single"

* double-quote-string-fixer

* --all-files

* apply

* Q

* drop double-quote-string-fixer

* --all-files

* apply pre-commit

* python3.11 -m poetry lock --no-update

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-27 11:32:14 +00:00
Boxuan Li
7d5856e36b local sandbox: Create workspace base during init (#1377) 2024-04-27 11:50:58 +08:00
Robert Brennan
fd9e598136 add options for controlling memory (#1364)
* add options for controlling memory

* Update agenthub/monologue_agent/utils/memory.py

Co-authored-by: Jim Su <jimsu@protonmail.com>

* move memory initialization switch back

* fix lint

* fix type

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-26 21:53:54 +00:00
Jim Su
c0adb55bfa Fix mock server (#1392) 2024-04-26 17:16:29 -04:00
Alex Bäuerle
e9a9186717 build(backend): change reload to ignore workspace correctly (#1393)
Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-26 18:19:18 +00:00
Jim Su
93d8ee1400 Render messages in markdown (#1391) 2024-04-26 14:05:17 -04:00
Boxuan Li
831e934dab Refactor: Use enum for config keys (#1376) 2024-04-26 10:26:01 -04:00
Engel Nyst
5a0ef2b5de Doc: update Azure guide with setting model in the UI (#1386)
* Update AzureLLMs.md

Add note about saving the actual model in the UI.

* Update AzureLLMs.md

Add note about saving the actual value in the UI.
2024-04-26 09:17:06 -04:00
sp.wack
0fb3d63406 fix/improve terminal hook (#1371) 2024-04-25 19:13:42 -04:00
Alex Bäuerle
65558df1f7 feat(frontend): multiple design changes (#1370) 2024-04-25 14:24:05 -07:00
Robert Brennan
5515c08624 update to 0.4.0 (#1362)
Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-25 19:27:25 +00:00
Robert Brennan
f407382a5a handle IsADirectory errors (#1365) 2024-04-25 15:15:06 -04:00
Xingyao Wang
110e7f0c4c make WORKSPACE_MOUNT_PATH as abspath (#1361) 2024-04-25 13:31:05 -04:00
Robert Brennan
a1a0767681 Revert "refactor(frontend): Terminal (#1315)" (#1360)
This reverts commit 27246aca7e.
2024-04-25 11:21:01 -04:00
Boxuan Li
e7b5ddfe06 Add integration test framework with mock llm (#1301)
* Add integration test framework with mock llm

* Fix MonologueAgent and PlannerAgent tests

* Remove adhoc logging

* Use existing logs

* Fix SWEAgent and PlannerAgent

* Check-in test log files

* conftest: look up under test name folder only

* Add docstring to conftest

* Finish dev doc

* Avoid non-determinism

* Remove dependency on llm embedding model

* Init embedding model only for MonologueAgent

* Add adhoc fix for sandbox discrepancy

* Test ssh and exec sandboxes

* CI: fix missing sandbox type

* conftest: Remove hack

* Reword comment for TODO
2024-04-25 10:56:53 -04:00
Leo
bf5a2afd83 Refactor browser style. (#1358)
* delete useless assets and css class.
* add waiting for page loaded (networkidle with 3s timeout)
2024-04-25 10:48:19 -04:00
Xingyao Wang
88397e0469 add back cache dir and default to /tmp (#1359) 2024-04-25 10:46:15 -04:00
Boxuan Li
c45e2ae4d9 Fix output strip behavior discrepancy among sandboxes (#1355) 2024-04-25 14:38:01 +08:00
Robert Brennan
5543fe2c3e fix up prompts (#1345) 2024-04-24 18:30:18 -04:00
Robert Brennan
bc84482c48 Update ssh_box.py (#1346) 2024-04-24 18:30:05 -04:00
Robert Brennan
6972ea0089 Go back to no host network by default (#1349) 2024-04-24 18:29:30 -04:00
Robert Brennan
43214a69a0 Better version management (#1313)
* better version management

* Update opendevin/config.py
2024-04-24 21:53:49 +00:00
Engel Nyst
2318ceae35 Send JSON parsing exceptions to LLM (#1342)
* Add malformed JSON where we don't even start finding actions

* Send any exception during JSON parsing back

* Use specific exceptions

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-24 17:51:09 -04:00
Engel Nyst
f4ce7e7862 Update description (#1337)
* Update base.py

* Update opendevin/action/base.py

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-24 17:50:51 -04:00
Engel Nyst
bd470e8076 Send the permission error to the llm (#1343)
* Send the permission error to the llm

* Update opendevin/action/fileop.py

* Update opendevin/action/fileop.py

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-24 17:50:32 -04:00
Xingyao Wang
fde0392457 Default for sandbox to use the host network (#1334)
* default to use host network

* make host network as default
2024-04-24 21:48:11 +00:00
Robert Brennan
1e95fa435d Microagents and Delegation (#1238)
* basic microagent structure

* start on jinja

* add instructions parser

* add action instructions

* add history instructions

* fix a few issues

* fix a few issues

* fix issues

* fix agent encoding

* fix up anon class

* prompt to fix errors

* less debug info when errors happen

* add another traceback

* add output to finish

* fix math prompt

* fix pg prompt

* fix up json prompt

* fix math prompt

* fix math prompt

* fix repo prompt

* fix up repo explorer

* update lock

* revert changes to agent_controller

* refactor microagent registration a bit

* create delegate action

* delegation working

* add finish action to manager

* fix tests

* rename microagents registry

* rename fn

* logspam

* add metadata to manager agent

* fix message

* move repo_explorer

* add delegator agent

* rename agent_definition

* fix up input-output plumbing

* fix tests

* Update agenthub/micro/math_agent/agent.yaml

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

* Update agenthub/delegator_agent/prompt.py

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

* Update agenthub/delegator_agent/prompt.py

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

* remove prompt.py

* fix lint

* Update agenthub/micro/postgres_agent/agent.yaml

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

* Update agenthub/micro/postgres_agent/agent.yaml

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

* fix error

---------

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-24 17:46:14 -04:00
Robert Brennan
8828d9836d use caching for docker builds (#1340)
* try pulling for cached layers

* use cache-from and cache-to

* empty commit

* add readme

* use specific cache tags

* also cache from main
2024-04-24 15:57:46 -04:00
Xia Zhenhua
d357fcff36 fix: sweagent prompt message SYSTEM_MESSAGE role set to user bug. (#1329)
Co-authored-by: aaren.xzh <aaren.xzh@antfin.com>
2024-04-24 15:51:52 -04:00
Robert Brennan
9241f713b0 pin macos (#1344) 2024-04-24 15:37:32 -04:00
Alex Bäuerle
2472840a56 style(frontend): remove the welcome message in the code editor (#1324)
* style(frontend): remove the welcome message in the code editor

I don't feel like we need this and it can be confusing since there is no file called "welcome". Also, the chat window already has a welcome message.

* lint

* remove empty checks

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-24 16:58:47 +00:00
Season
ab3e18667b feat: LocalLLM docs without docker (#1269)
* feat:  start Devin without Docker locally

* chore: make consistent model choices

* chore: more detailed explanation for using litellm server as walkaround

* chore: simply pr
2024-04-24 16:47:20 +00:00
Robert Brennan
236b7bf6ea refactor error handling so not all exceptions are caught (#1296)
* refactor error handling so not all exceptions are caught

* revert

* Send the failed decoding back to the LLM (#1322)

* fix quotes

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-24 16:44:06 +00:00
Alex Bäuerle
dc73954cce build: when running in dev mode, reload the poetry server whenever a … (#1323)
* build: when running in dev mode, reload the poetry server whenever a file changes

* only reload for specific directories

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-24 09:34:40 -07:00
Xingyao Wang
9b48f63544 Fix build error (#1339)
* add initial version of swe-agent plugin;

* rename swe cursors

* split setup script into two and create two requirements

* print SWE-agent command documentation

* update swe-agent to default to no custom docs

* update dockerfile with dependency from swe-agent

* make env setup a separate script for .bashrc source

* fix swe-tool plugins;
add missing _split_string

* remove import for temporarily fix (will add back in another pr)
2024-04-24 12:25:18 -04:00
Xingyao Wang
e6ecbd5510 fix(sandbox): mount_dir for ssh_box (#1333)
* fix mount_dir for ssh_box

* move logic to config.py
2024-04-24 12:10:14 -04:00
sp.wack
2268529b75 fix test imports (#1327) 2024-04-24 09:09:48 -07:00
sp.wack
a0dd5880d7 improve formatting and extend to include i18n scripts (#1328) 2024-04-24 09:09:05 -07:00
Christian Balcom
b48f440476 Add sanitization against malformed paths used in fileop actions. Added tests for resolve_path. (#1338) 2024-04-24 15:46:16 +00:00
Xingyao Wang
a0e8fcb19a Add SWE-agent tools as sandbox plugins (#1305)
* add initial version of swe-agent plugin;

* rename swe cursors

* split setup script into two and create two requirements

* print SWE-agent command documentation

* update swe-agent to default to no custom docs

* update dockerfile with dependency from swe-agent

* make env setup a separate script for .bashrc source

* fix swe-tool plugins;
add missing _split_string

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-24 22:50:14 +08:00
மனோஜ்குமார் பழனிச்சாமி
99829f95fd Applied npm patch for build errors (#1326)
* npm patch

https://github.com/npm/cli/issues/7231#issuecomment-2062032218

* Delete huge unnecessary tools folder
2024-04-24 09:24:48 -04:00
Xingyao Wang
0b6ab238f9 Update Development.md (#1330) 2024-04-24 17:05:27 +08:00
Xingyao Wang
3eea6de546 fix(sandbox): SSH box .execute misalign issue (#1304)
* fix weird behavior of pxssh where the output would not flush correctly

* make ssh box can handle exit_code properly as well

* Update opendevin/sandbox/docker/ssh_box.py

Co-authored-by: Robert Brennan <accounts@rbren.io>

* fix typo

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-24 12:27:44 +08:00
sp.wack
27246aca7e refactor(frontend): Terminal (#1315)
* create new modal for loading previous session

* style and replace modal

* retire old components and group modals into folder

* Utilise i18n for text content and add en translations

* prevent modal from being dismissed via the backdrop

* reference issue that its fixing

* fix incorrect role in tests

* initial commit'

* add output support

* update addon-fit library and mgirate to useXTerm

* add test todos

* move useXTerm to hooks folder

* Fix import path error

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-24 02:34:46 +00:00
Larkin Williams-Capone
ba556ea4d8 Add docs for using google llms (#1321) 2024-04-23 22:22:18 +00:00
Alex Bäuerle
f40e91b41b refactor: move leftnav and agent controls to new play bar (#1317)
* refactor: move leftnav and agent controls to new play bar

* layout change
2024-04-23 15:17:43 -07:00
Engel Nyst
d1551c3097 Add checks to stop infinite loops (#1293)
* Add checks to stop infinite loops

* Send an AgentErrorObservation for the user to see an oops loop

* (NullAction, Obs) problem should be (NullAction, error Obs)

* Merge the two with AgentErrorObs.

* Update opendevin/controller/agent_controller.py
2024-04-23 17:52:32 -04:00
Joen Wallenstrøm (UnLogick)
f6dcbeaa1d Add ollama wsl section to LocalLLMs.md (#1318) 2024-04-23 21:40:28 +00:00
Alex Bäuerle
620bbb38cf feat: disable settings while task is running (#1291)
* feat: disable settings while task is running

* test files

* tests

* test
2024-04-23 14:20:49 -07:00
Vasek Mlejnsky
a2a86e84f0 Integrate copy_to in E2BBox (#1298)
* initialize plugin definition

* initialize plugin definition

* simplify mixin

* further improve plugin mixin

* add cache dir for pip

* support clean up cache

* add script for setup jupyter and execution server

* integrate JupyterRequirement to ssh_box

* source bashrc at the end of plugin load

* add execute_cli that accept code via stdin

* make JUPYTER_EXEC_SERVER_PORT configurable via env var

* increase background cmd sleep time

* Update opendevin/sandbox/plugins/mixin.py

Co-authored-by: Robert Brennan <accounts@rbren.io>

* add mixin to base class

* make jupyter requirement a dataclass

* source plugins only when >0 requirements

* add `sandbox_plugins` for each agent & have controller take care of it

* update build.sh to make logs available in /opendevin/logs

* switch to use config for lib and cache dir

* fix permission issue with /workspace

* use python to implement execute_cli to avoid stdin escape issue

* wait until jupyter is avaialble

* support plugin via copying instead of mounting

* Fix linter issue

---------

Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-23 07:55:27 -04:00
Leo
0a2e90dece Disable additional Docker metadata to more compatible with OCI spec. (#1302) 2024-04-23 07:55:00 -04:00
Xia Zhenhua
747ac23cd0 fix: conftest.py comment bug. (#1303)
Co-authored-by: aaren.xzh <aaren.xzh@antfin.com>
2024-04-23 07:51:33 -04:00
Engel Nyst
960f17a565 Bug with sshd (#1297)
* Workaround for a bug with sshd

* Update containers/sandbox/Dockerfile

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

---------

Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
2024-04-23 01:33:44 +00:00
Xingyao Wang
fc5e075ea0 feat(sandbox): Implementation of Sandbox Plugin to Support Jupyter (#1255)
* initialize plugin definition

* initialize plugin definition

* simplify mixin

* further improve plugin mixin

* add cache dir for pip

* support clean up cache

* add script for setup jupyter and execution server

* integrate JupyterRequirement to ssh_box

* source bashrc at the end of plugin load

* add execute_cli that accept code via stdin

* make JUPYTER_EXEC_SERVER_PORT configurable via env var

* increase background cmd sleep time

* Update opendevin/sandbox/plugins/mixin.py

Co-authored-by: Robert Brennan <accounts@rbren.io>

* add mixin to base class

* make jupyter requirement a dataclass

* source plugins only when >0 requirements

* add `sandbox_plugins` for each agent & have controller take care of it

* update build.sh to make logs available in /opendevin/logs

* switch to use config for lib and cache dir

* fix permission issue with /workspace

* use python to implement execute_cli to avoid stdin escape issue

* wait until jupyter is avaialble

* support plugin via copying instead of mounting

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-23 08:45:53 +08:00
Engel Nyst
220dac926e Note on model name (#1295) 2024-04-22 22:53:16 +00:00
Xingyao Wang
e84125de6f Symlink python3 to python in Sandbox (#1286)
* symlink py3 to py

* remove service ssh start
2024-04-23 01:05:05 +08:00
Jim Su
b0c3bca915 refactor(frontend): Refactor imports to use absolute path (#1288)
* Refactor imports to use absolute path

* Rename absolute import symbol from src to #

* Refactor imports to use absolute path

* Rename absolute import symbol from src to #

* Remove unused LoadMessageModal
2024-04-22 16:29:03 +00:00
Engel Nyst
39a851bd95 Message property sent to llm (#1273)
* Add to_memory for observations

* Don't send 'message' property to the llm from observation; fix other agents
2024-04-22 12:14:45 -04:00
sp.wack
f55f7be2e6 update readme to include testing script and documentation (#1272) 2024-04-22 08:54:28 -07:00
sp.wack
0669b27522 refactor(frontend): load previous session modal (#1284)
* create new modal for loading previous session

* style and replace modal

* retire old components and group modals into folder

* Utilise i18n for text content and add en translations

* prevent modal from being dismissed via the backdrop

* reference issue that its fixing

* fix incorrect role in tests

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-22 08:46:32 -07:00
Jim Su
3193014ed8 Correct workspace directory config variable in README (#1282) 2024-04-22 10:32:48 -04:00
மனோஜ்குமார் பழனிச்சாமி
cfbd482498 Added playwright commands (#1184)
* post playwright commands

* post playwright commands with deps

* post playwright commands

* post playwright commands with deps

* Put playwright install in the correct place

---------

Co-authored-by: Graham Neubig <neubig@gmail.com>
2024-04-22 09:43:08 -04:00
Beichen Ma
2242702cf9 feat add prerequisites validation (#943)
* feat add prerequisites validation

* fix error

* Update Makefile

Co-authored-by: Robert Brennan <accounts@rbren.io>

* change awk with sek

* fix typo

* fix error

* fix linux error

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-22 03:40:56 +00:00
#Einswilli
157278704e style: Refactor get_prompt function for readability and maintainability (#1051)
* AgentHub.planner_agent.prompt: Optimize the get_prompt function by using a dictionary to map ActionType to hints, thus avoiding repetitions and making the code more readable and maintainable.

* Update prompt.py

* Fix Lint issues in prompt.py

* Fix lint issues in prompt.py

* Fix Lint issues in prompt.py

* Fix Lint issues prompt.py

* Remove trailing whitespace

---------

Co-authored-by: Alex Bäuerle <alex@a13x.io>
Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Jim Su <jimsu@protonmail.com>
2024-04-22 03:32:31 +00:00
Alex Bäuerle
ffa62b9928 style: flip controls and status message for chat (#1278) 2024-04-22 03:21:02 +00:00
Geun, Lim
bc23c9429c update ko-KR translation (#1258)
update ko-KR translation
2024-04-22 03:18:01 +00:00
Robert Brennan
8310cf6e23 Update README.md (#1279) 2024-04-21 23:07:49 -04:00
Engel Nyst
464bf7ee23 Tweak connect exceptions (#1120)
* Clean up manual sleep

* Add default retries and document them.

* Add doctrings to llm

* Add exponential backoff for rate limiting errors

* Get embeddings for the action and its own content, not the user message

* Add a few bad exceptions to stop loop

* Stop loop when the step has no action

* Add action with content, no message, to history

* make retry settings customizable

* fix condense to stop the loop for the same reasons as completion

* Add 500-504 exception to retries

* document the retry variables

* Add retries and limits for embeddings. Replaces llama-index hard-coded decorator.

* Rename to retry_min_wait and retry_max_wait
2024-04-22 04:00:01 +02:00
sp.wack
1f2a845feb refactor(frontend): settings modal (#1256)
* allow arrow functions for components

* initial commit - BaseModal

* initial commit - SettingsForm

* extend tests and component

* extend to support language

* refactor tests

* move files and separate component/tests

* extend functionality

* refactor

* major refactor and flip flops

* add tests

* fix styles and names

* add loading state

* remove old SettingModal

* refactor component into smaller ones

* fix model input

* revert eslint rule to allow multiple function definitions for components and remove unused helper function

* add new i18n key for language placeholder

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-21 17:54:29 -07:00
yaroslavMain
454e9613b0 Add pydantic (#868)
* Update action.py

* Update observation.py

* Update observation.py

* Update action.py

* Update observation.py

* Update opendevin/schema/action.py

* Update action.py

* Update observation.py

* Update action.py

* Update observation.py

* Update observation.py

* Update observation.py

* Update observation.py

* Update action.py

* Update action.py

* Update action.py

* Update observation.py

* Update observation.py

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-21 19:41:04 +00:00
Andrii Stadnik
900de8728c Update LocalLLMs.md (#1267) 2024-04-21 19:23:19 +00:00
Boxuan Li
7bd5417b95 DogFood: instruct agent to read from diff file (#1259) 2024-04-21 15:21:09 -04:00
Yoni
5a4913224a Use /bin/bash as the shell for Makefile (#1264) 2024-04-21 15:17:35 -04:00
Xingyao Wang
dffbeec45a Add SANDBOX_WORKSPACE_DIR into config (#1266)
* Add SANDBOX_WORKSPACE_DIR into config

* Add SANDBOX_WORKSPACE_DIR into config

* fix occurence of /workspace
2024-04-21 15:17:10 -04:00
Leo
0e572c3e41 feat: support tls. #1234 (#1248)
* feat: support tls.

* update the frontend README.

* Update frontend/README.md

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

---------

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
2024-04-21 14:50:48 -04:00
Arno.Edwards
d692a72bf3 fix: correct port number in LocalLLMs.md (#1252)
* fix: correct port number in `LocalLLMs.md`

- improve details and linting

* Update docs/guides/LocalLLMs.md

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-20 14:34:32 +00:00
Coenraad Loubser
b5da25e9cc Update README.md:--add-host host.docker.internal=host-gateway fixes #1156 (#1250) 2024-04-20 09:49:59 -04:00
Boxuan Li
1210eea48d DogFood: Use OpenDevin to review PR (#1223)
* DogFood: Use OpenDevin to review PR

* Use diff rather than patch

* Fix prompt

* Return if label not present

* Don't write review to environment variable

* Fix label check

* Use better name for labels
2024-04-20 09:25:36 -04:00
Boxuan Li
cf3372a5fe Fix arg parser help message display (#1247)
* Fix arg parser help message display

* Add unittest
2024-04-20 09:23:19 -04:00
Alex Bäuerle
49ff317b50 feat: add change indicator to workspace tabs (#1236) 2024-04-20 09:23:07 -04:00
Alex Bäuerle
6340cd9aed style: remove warning about useEffect (#1232)
This warning was appended to every PR. While the warningn typically makes sense, in this case we intentionally don't want to rerun if `desiredState` changes.
2024-04-20 09:20:03 -04:00
Robert Brennan
3c85ece23e Add troubleshooting document and issue template (#1243)
* add troubleshooting doc

* add link to troubleshooting

* mark required

* unmark required

* more required stuff

* add nonetype issue

* Update .github/ISSUE_TEMPLATE/bug_template.yml

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>

* Update .github/ISSUE_TEMPLATE/bug_template.yml

---------

Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
2024-04-20 13:05:45 +00:00
மனோஜ்குமார் பழனிச்சாமி
0356f6ec89 Azure LLM fix (#1227)
* azure embedding fix

* corrected embedding config

* fixed doc
2024-04-20 01:05:14 +02:00
Robert Brennan
3988443705 Fix local LLM guide (#1240)
* Fix local LLM guide

* Update docs/guides/LocalLLMs.md
2024-04-19 16:22:41 -04:00
Robert Brennan
7675b3ca34 fix manager ref (#1241) 2024-04-19 16:18:30 -04:00
Robert Brennan
d61fdb8bba less debug info when errors happen (#1233)
* less debug info when errors happen

* add another traceback

* better prompt debugs

* remove unuse arg
2024-04-19 15:15:38 -04:00
Alex Bäuerle
0a59d46ca3 feat: align status bar with control bar (#1235) 2024-04-19 15:15:07 -04:00
Vasek Mlejnsky
76b81ca0ed Integrate E2B sandbox as an alternative to a Docker container (#727)
* add e2b sandbox [wip]

* Install e2b package

* Add basic E2B sandbox integration

* Update dependencies and fix command execution in E2BSandbox

* Udpate e2b

* Add comment

* Lint

* Remove unnecessary type conversion

* Lint

* Fix linting

* Resolve comments

* Update opendevin/action/fileop.py

* Update opendevin/action/fileop.py

* Fix log

* Update E2B readme

* poetry lock

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-19 14:21:58 -04:00
Alex Bäuerle
e6d91affc6 refactor: remove the previously implemented shallow file fetch (#1231)
Removing because the new filetree implementation is fast enough as is and fetching partial trees adds a lot of complexity that we probably don't need. For example, if we still want to automatically open the tree location of a changed file, we need to make sure all the parent folders are fetched first.
2024-04-19 17:25:40 +00:00
sp.wack
e82543747c fix(frontend): rebuild the file explorer; goodbye lag (#1221)
* initial commit

* separate tests and add new prop

* initial commit

* initial commit - file explorer

* update test and code

* adjustments and replacements

* update folder name

* fix import

* refactor for better readability and extending

* replace TreeNode type with existing WorkspaceFile

* refactor to have workspace support only one root node

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-19 17:09:21 +00:00
Alex Bäuerle
959d91c9d6 feat: implement basic planner UI (#1173)
* feat: implement basic planner UI

* update planner UI

* lint

* fix type

* fixes

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-19 13:09:14 -04:00
Xingyao Wang
871eefe801 Revert "feat(sandbox): Add Jupyter Kernel for Interactive Python Interpreter for Sandbox (#1215)" (#1229)
This reverts commit 492feecb67.
2024-04-19 16:49:24 +00:00
Leo
34286fabcc fix ghcr workflow throw error when in the fork repo. (#1203)
* fix ghcr workflow throw error when in the fork repo.

* Split DOCKER_REPOSITORY for cleaner.
2024-04-19 11:06:25 -04:00
sp.wack
b9bed7da8d test(frontend): create new test and update existing ones (#1190)
* create new test and update existing

* Prevent empty/undefined values from being set
2024-04-19 11:05:14 -04:00
Robert Brennan
7c3ea9fa08 Fix up documentation for specific LLMs (#1217)
* Update LOCAL_LLM_GUIDE.md

* Create WINDOWS_WSL_GUIDE.md

* update docs

* Update docs/Agents.md

* update windows docs

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-19 10:47:54 -04:00
Xingyao Wang
492feecb67 feat(sandbox): Add Jupyter Kernel for Interactive Python Interpreter for Sandbox (#1215)
* add initial version of py interpreter

* fix bug

* fix async issue

* remove debugging print statement

* initialize kernel & update printing

* fix port mapping

* uncomment debug lines

* fix poetry lock

* make jupyter py interpreter into a subclass
2024-04-19 16:55:51 +08:00
sp.wack
ca1d53c161 fix(frontend): improve the <Input /> component (#1195)
* create a better chat input component

* remove the useInputComposition hook
2024-04-18 16:19:57 -07:00
Engel Nyst
fe3d4b129d Path in observation (#1216)
* create observation with the workspace path

* rename the whole path whole_path
2024-04-18 15:06:51 -04:00
RaGe
2bf34093b0 Add new endpoint for shallow file listing - backend only (#1174)
* Add /api/list-files endpoint

Returns files at the path specified by query parameter relpath, relative to base path, with a depth of 1

* Add method in services
2024-04-18 11:22:39 -07:00
Robert Brennan
9e85550b46 fix initialize (#1214) 2024-04-18 13:19:33 -04:00
Robert Brennan
2826e39056 fix file writes (#1213)
* fix file writes

* Update opendevin/action/fileop.py
2024-04-18 12:47:52 -04:00
Leo
226fb43fbe fix wrong logo path. (#1211) 2024-04-18 11:53:11 -04:00
Robert Brennan
9d5bc6b332 Update README.md (#1212) 2024-04-18 11:49:17 -04:00
Robert Brennan
af262c17f1 properly exit loop on finish (#1210) 2024-04-18 11:03:29 -04:00
Robert Brennan
6b0408d47c use threading to insert docs into the db (#1191) 2024-04-18 11:31:39 +00:00
Robert Brennan
74c9b58d1b handle not found errors inside fileop (#1188) 2024-04-18 07:16:48 -04:00
Leo
1356da8795 feat: support controlling agent task state. (#1094)
* feat: support controlling agent task state.

* feat: add agent task state to agent status bar.

* feat: add agent task control bar to FE.

* Remove stop agent task action.

* Merge pause and resume buttons into one button; Add loading and disabled status for action buttons.

* Apply suggestions from code review

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-18 11:09:00 +00:00
Anthony Elkommos Youssef
f7fd925f30 i18n : Add Arabic (#997)
* i18n : Add Arabic

* add ar translation

* chatSlice Cleanup

* simplified 2 conditions

* RTL Support & French:
* Added French as a language
* Added RTL support for Arabic

* Merge

* Merge

* Merge

* Merge

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-18 11:01:01 +00:00
Edwards.Arno
94831083c3 feat: improve credential validation and simplify response in get_token (#1155)
* feat: improve credential validation and simplify response in get_token

* Apply suggestions from code review

* Update opendevin/server/listen.py

* fix typo

* Apply suggestions from code review

* Update listen.py to remove unused import.

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-18 07:00:24 -04:00
从零开始学AI
396b6a62a9 [doc] add README-zh.md (#1116) 2024-04-18 06:59:16 -04:00
Engel Nyst
caabfab7e2 Property 'message' sent to llm (#1198)
* Add action with content, no message, to history

* fix to_memory(), add it to serialization tests

* Actions without 'message' in completion too
2024-04-18 06:51:07 -04:00
Alex Bäuerle
51149780ac fix: fix path traversal vulnerability (#1199) 2024-04-18 06:49:37 -04:00
sp.wack
426f387123 setup env for controlled integration tests with redux (#1180) 2024-04-17 10:55:17 -07:00
Robert Brennan
9fd7068204 Fix for setting LLM model, frontend settings refactor (#1169)
* simplify frontend settings management

* add debug info to llm.py

* always reinitialize agent

* remove old config stuff

* delint

* fix first initialize event

* refactor settings management

* remove logs

* change endpoint to remove litellm reference

* actually fix socket issues

* refactor a bit

* delint

* remove isFirstRun

* delint

* delint python

* fix export

* fix up socket handshake

* fix types

* fix lint errors

* delint

* fix test names

* moar lint

* fix build errors

* remove newline

* Update frontend/src/services/settingsService.test.ts

* Update frontend/src/services/settingsService.test.ts
2024-04-17 17:49:38 +00:00
மனோஜ்குமார் பழனிச்சாமி
18348911c2 Use python3.11 as default (#885)
* use python3.11

* use python3.11

* removed 3.12

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-17 17:28:16 +00:00
Justus
8472436fe8 Syntax highlighting changes based on what file you are editing (#1176)
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-17 17:10:45 +00:00
Jack Quimby
16fc728696 feat: SWE Agent Implementation (#846)
* Merge branch 'main' of https://github.com/JayQuimby/OpenDevin

* Using commands.sh for ACI

* parsing, prompting, and actions modifications

* added start and end index to read and write

* bug fixes and test updates

* Lint code changes to ensure code is proper

* State management, bugs, prompts

* Prompt Engineering

* exception handling

* big fixes

* more bug fixes

* merge conflicts

* Renamed SWEAgent, basic tests, bug fixes

* prompt changes, bug fixes

* merge conflicts

* merge conflict

* start and end line for read and write

* more merge conflicts

* env error

* linter error

* read fixed, prompt change, example added

* added x_line:end read operation

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-17 12:19:00 -04:00
Yoni
c8c1eed5be Reset agent between tasks (#1182)
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-17 12:13:54 -04:00
Xia Zhenhua
60a92a07c1 modify all api response to the same format (JSONResponse with status_code and content set) (#1175)
* feat: modify all api response to JSONResponse with status_code and content.

* refactor: make status-code 200 response more concise.

---------

Co-authored-by: aaren.xzh <aaren.xzh@antfin.com>
2024-04-17 12:13:32 -04:00
மனோஜ்குமார் பழனிச்சாமி
a0fee17388 less verbose (#1183) 2024-04-17 11:41:18 -04:00
Robert Brennan
b654b00aa1 Update README.md (#1181) 2024-04-17 09:11:45 -04:00
Boxuan Li
aed82704a9 Fix python linter inconsistent behaviour with quotes (#1112)
* This has been a headache for a long time, and we had #1071 and #1100 with the hope to fix the inconsistent behaviour across linters and environments. However, we recently found out that double-quote-string-fixer plugin in pre-commit-hook has inconsistent behaviour on python 3.11 and 3.12. See discussion here. This is sad because while this plugin enforces single quote behaviour with 3.11, it doesn't always enforce so with 3.12. Specifically, with fstr syntax, this plugin allows both single quotes and double quotes with python 3.12.

The problem is, some developers have black linter installed/integrated with their IDE, which is probably the most popular linter in python world (ranked by GitHub stars). This linter insists on always using double quotes. Now we have black and double-quote-string-fixer fight each other (iff the developer uses python 3.12) for some quotes (fstr syntax).

After a lot of research, I couldn't find a way to enforce single quote behaviour without introducing a new dependency, flake8, together with a plugin for it to enforce quotes' behavior. I believe it's better off introducing the more popular black if we have to introduce a new linter. Since black and autopep8 sometimes fight each other, and they mostly overlap, I further remove autopep8.

The unfortunate consequence of this PR is that I had to revert all single quotes back to double quotes. This might cause some inconvenience to existing PRs as they have to resolve conflicts, but I believe the headache will be gone soon. That being said, I am open to abandon this PR if anyone has a better idea to solve the headache.

* Remove black

* Prevent black from changing quotes

* Use flake8 to enforce single quotes

* Fix quotes in config.py

* Add back autopep8

* Add make lint to run linters
2024-04-17 00:12:52 -04:00
sp.wack
edeea95e7d replace Jest with Vitest as the frontend test runner (#1163) 2024-04-16 21:12:11 -07:00
Robert Brennan
a663302ba2 Refactor actions a bit (#1165)
* refactor to action_manager

* make all actions awaitable

* move task logic into tasks

* Update opendevin/action/tasks.py
2024-04-16 21:29:38 -04:00
Engel Nyst
1338248310 Document LLM prompt and response logging. (#1166) 2024-04-16 17:29:50 -04:00
Robert Brennan
d34a9db04f Run docker publish on tags (#1162)
* run ghcr on tags

* Update .github/workflows/ghcr.yml

Co-authored-by: RaGe <foragerr@users.noreply.github.com>

---------

Co-authored-by: RaGe <foragerr@users.noreply.github.com>
2024-04-16 13:27:31 -04:00
Alex Bäuerle
7710112ae2 fix: fix delete messages endpoint url (#1140)
Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-16 17:08:18 +00:00
Alex Bäuerle
6b007c163b fix: make typing chat bubble adhere to max width (#1142) 2024-04-16 10:02:04 -07:00
Engel Nyst
1115b60a74 Logging additions and fixes (#1139)
* Refactor print_to_color into a color formatter

misc fixes

catch ValueErrors and others from Router initialization

add default methods

* Tweak console log formatting, clean up after rebasing exceptions out

* Fix prompts/responses

* clean up

* keep regular colors when no msg_type

* fix filename

* handle file log first

* happy mypy

* ok, mypy

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-16 12:55:22 -04:00
sp.wack
5881857d5c test(frontend): add unit tests (#1076)
* test(frontend): add unit tests for getCachedConfig

* test(frontend): add unit tests for getCachedConfig

* add unit test for the useTypingEffect hook

* add unit test for the useInputComposition hook

* create unit test for auth service

* remove outdated and failing component test

* create unit test for session service

* break down saveSettings into smaller functions for testability and create unit test for new mergeAndUpdateSettings

---------

Co-authored-by: Robert Brennan <accounts@rbren.io>
2024-04-16 12:49:56 -04:00
414 changed files with 38804 additions and 4816 deletions

2
.gitattributes vendored
View File

@@ -1 +1 @@
*.ipynb linguist-vendored
*.ipynb linguist-vendored

View File

@@ -1,41 +0,0 @@
---
name: Bug Report
about: Report a problem with OpenDevin
title: ''
labels: 'bug'
assignees: ''
---
<!-- You MUST fill out this template. We will close issues that don't include enough information to reproduce -->
#### Describe the bug
<!-- a short description of the problem -->
#### Setup and configuration
**Current version**:
<!-- run `git log -n 1` to see this -->
```bash
```
**My operating system**:
<!-- tell us everything about your environment -->
**My environment vars and other configuration** (be sure to redact API keys):
```bash
```
**My model and agent** (you can see these settings in the UI):
* Model:
* Agent:
**Commands I ran to install and run OpenDevin**:
```
```
**Steps to Reproduce**:
1.
2.
3.
**Logs, error messages, and screenshots**:
#### Additional Context

69
.github/ISSUE_TEMPLATE/bug_template.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Bug
description: Report a problem with OpenDevin
title: '[Bug]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: Thank you for taking the time to fill out this bug report. We greatly appreciate your effort to complete this template fully. Please provide as much information as possible to help us understand and address the issue effectively.
- type: checkboxes
attributes:
label: Is there an existing issue for the same bug?
description: Please check if an issue already exists for the bug you encountered.
options:
- label: I have checked the troubleshooting document at https://opendevin.github.io/OpenDevin/modules/usage/troubleshooting
required: true
- label: I have checked the existing issues.
required: true
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: Provide a short description of the problem.
validations:
required: true
- type: textarea
id: current-version
attributes:
label: Current Version
description: What version are you using? If you're running in docker, tell us the tag you're using (e.g. ghcr.io/opendevin/opendevin:0.3.1).
render: bash
validations:
required: true
- type: textarea
id: config
attributes:
label: Installation and Configuration
description: Please provide any commands you ran and any configuration (redacting API keys)
render: bash
validations:
required: true
- type: textarea
id: model-agent
attributes:
label: Model and Agent
description: What model and agent are you using? You can see these settings in the UI by clicking the settings wheel.
placeholder: |
- Model:
- Agent:
- type: textarea
id: repro-steps
attributes:
label: Reproduction Steps
description: Please list the steps to reproduce the issue.
placeholder: |
1.
2.
3.
- type: textarea
id: additional-context
attributes:
label: Logs, Errors, Screenshots, and Additional Context
description: Please add any additional context about the problem here.

View File

@@ -1,47 +0,0 @@
name: Build & Run Tests
on: [push, pull_request]
jobs:
on-macos:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install & Start Docker
run: |
brew install colima docker
colima start
- name: Install and configure Poetry
uses: snok/install-poetry@v1
with:
version: latest
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest ./tests
on-linux:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python3 -
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest ./tests

58
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Deploy Docs to GitHub Pages
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
name: Build Docusaurus
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
cache-dependency-path: docs/package-lock.json
- name: 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: cd docs && npm ci
- name: Build website
run: cd docs && npm run build
- name: Upload Build Artifact
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
with:
path: docs/build
deploy:
name: Deploy to GitHub Pages
needs: build
if: github.ref == 'refs/heads/main'
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
# Deploy to the github-pages environment
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

30
.github/workflows/dummy-agent-test.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Run e2e test with dummy agent
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up environment
run: |
curl -sSL https://install.python-poetry.org | python3 -
poetry install --without evaluation
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/core/main.py -t "do a flip" -m ollama/not-a-model -d ./workspace/ -c DummyAgent

View File

@@ -1,8 +1,16 @@
name: Publish Docker Image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches: [ '**' ]
branches:
- main
tags:
- '*'
pull_request:
workflow_dispatch:
inputs:
reason:
@@ -13,7 +21,11 @@ on:
jobs:
ghcr_build_and_push:
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.inputs.reason != ''
permissions:
contents: read
packages: write
strategy:
matrix:
image: ["app", "evaluation", "sandbox"]
@@ -22,6 +34,22 @@ jobs:
- name: checkout
uses: actions/checkout@v4
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -29,8 +57,26 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@v3
- name: Log-in to ghcr.io
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- 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 }}
run: ./containers/build.sh ${{ matrix.image }} --push
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.fork"
run: |
./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }}
docker_build_success:
name: Docker Build Success
runs-on: ubuntu-latest
needs: ghcr_build_and_push
steps:
- run: echo Done!

View File

@@ -1,9 +1,18 @@
name: Lint
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
pull_request:
jobs:
lint-frontend:
name: Lint frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -28,11 +37,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up python
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: 'pip'
- name: Install pre-commit
run: pip install pre-commit==3.7.0
- name: Run pre-commit hooks
run: pre-commit run --files opendevin/**/* agenthub/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
if: github.ref != 'refs/heads/main'
run: |
git fetch https://github.com/OpenDevin/OpenDevin.git main:main && \
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 ./dev_config/python/.pre-commit-config.yaml

69
.github/workflows/review-pr.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Use OpenDevin to Review Pull Request
on:
pull_request:
types: [synchronize, labeled]
permissions:
contents: write
pull-requests: write
jobs:
dogfood:
if: contains(github.event.pull_request.labels.*.name, 'review-this')
runs-on: ubuntu-latest
container:
image: ghcr.io/opendevin/opendevin
volumes:
- /var/run/docker.sock:/var/run/docker.sock
steps:
- name: install git, github cli
run: |
apt-get install -y git gh
git config --global --add safe.directory $PWD
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref }} # check out the target branch
- name: Download Diff
run: |
curl -O "${{ github.event.pull_request.diff_url }}" -L
- name: Write Task File
run: |
echo "Your coworker wants to apply a pull request to this project. Read and review ${{ github.event.pull_request.number }}.diff file. Create a review-${{ github.event.pull_request.number }}.txt and write your concise comments and suggestions there." > task.txt
echo "" >> task.txt
echo "Title" >> task.txt
echo "${{ github.event.pull_request.title }}" >> task.txt
echo "" >> task.txt
echo "Description" >> task.txt
echo "${{ github.event.pull_request.body }}" >> task.txt
echo "" >> task.txt
echo "Diff file is: ${{ github.event.pull_request.number }}.diff" >> task.txt
- name: Run OpenDevin
env:
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SANDBOX_TYPE: exec
run: |
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
rm task.txt
- name: Check if review file is non-empty
id: check_file
run: |
ls -la
if [[ -s review-${{ github.event.pull_request.number }}.txt ]]; then
echo "non_empty=true" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Create PR review if file is non-empty
env:
GH_TOKEN: ${{ github.token }}
if: steps.check_file.outputs.non_empty == 'true'
run: |
gh pr review ${{ github.event.pull_request.number }} --comment --body-file "review-${{ github.event.pull_request.number }}.txt"

View File

@@ -0,0 +1,76 @@
name: Run Integration Tests
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
pull_request:
jobs:
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]
agent: ["SWEAgent", "PlannerAgent", "MonologueAgent", "CodeActAgent"]
sandbox: ["ssh", "exec"]
include:
- agent: "MonologueAgent"
embedding-model: "local"
- agent: "MonologueAgent"
# sufficient to have one agent testing against local sandbox
sandbox: "local"
embedding-model: "local"
- agent: "SWEAgent"
embedding-model: "none"
- agent: "PlannerAgent"
embedding-model: "none"
- agent: "CodeActAgent"
embedding-model: "none"
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install
- name: Build Environment
run: make build
- name: Run Integration Tests
env:
SANDBOX_TYPE: ${{ matrix.sandbox }}
AGENT: ${{ matrix.agent }}
MAX_ITERATIONS: 10
LLM_EMBEDDING_MODEL: ${{ matrix.embedding-model }}
run: |
rm -rf workspace
mkdir workspace
WORKSPACE_BASE="$GITHUB_WORKSPACE/workspace" \
WORKSPACE_MOUNT_PATH="$GITHUB_WORKSPACE/workspace" \
poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml \
-s ./tests/integration
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test_matrix_success:
name: All Integration Tests Passed
runs-on: ubuntu-latest
needs: [integration-tests]
steps:
- run: echo Done!

View File

@@ -1,20 +0,0 @@
name: Run Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Set up environment
run: |
curl -sSL https://install.python-poetry.org | python3 -
poetry install --without evaluation
- name: Run tests
run: |
poetry run pytest ./tests

92
.github/workflows/run-unit-tests.yml vendored Normal file
View File

@@ -0,0 +1,92 @@
name: Run Unit Tests
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
pull_request:
jobs:
test-on-macos:
name: Test on macOS
runs-on: macos-13
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install
- name: Install & Start Docker
run: |
brew install colima docker
colima start
# For testcontainers to find the Colima socket
# https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running
sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml ./tests/unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test-on-linux:
name: Test on Linux
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Python dependencies using Poetry
run: poetry install --without evaluation
- name: Build Environment
run: make build
- name: Run Tests
run: poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml ./tests/unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test_matrix_success:
name: All Mac/Linux Tests Passed
runs-on: ubuntu-latest
needs: [test-on-macos, test-on-linux]
steps:
- run: echo Done!

View File

@@ -10,8 +10,8 @@ permissions:
issues: write
jobs:
open-devin:
if: github.event.label.name == 'dogfood-this'
dogfood:
if: github.event.label.name == 'solve-this'
runs-on: ubuntu-latest
container:
image: ghcr.io/opendevin/opendevin
@@ -43,7 +43,7 @@ jobs:
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SANDBOX_TYPE: exec
run: |
python ./opendevin/main.py -d "./" -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

1
.gitignore vendored
View File

@@ -57,7 +57,6 @@ cover/
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

View File

@@ -85,5 +85,6 @@ Please refer to the README in each module:
- [mock server](./opendevin/mock/README.md)
## Tests
TODO: make sure code pass the test before submit.
Please navigate to `tests` folder to see existing test suites.
At the moment, we have two kinds of tests: `unit` and `integration`. Please refer to the README for each test suite. These tests also run on CI to ensure quality of
the project.

View File

@@ -12,6 +12,20 @@ This guide is for people working on OpenDevin and editing the source code.
Make sure you have all these dependencies installed before moving on to `make build`.
#### Develop without sudo access
If you want to develop without system admin/sudo access to upgrade/install `Python` and/or `NodeJs`, you can use `conda` or `mamba` to manage the packages for you:
```bash
# Download and install Mamba (a faster version of conda)
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh
# Install Python 3.11, nodejs, and poetry
mamba install python=3.11
mamba install conda-forge::nodejs
mamba install conda-forge::poetry
```
### 2. Build and Setup The Environment
- **Build the Project:** Begin by building the project, which includes setting up the environment and installing dependencies. This step ensures that OpenDevin is ready to run smoothly on your system.
@@ -30,7 +44,7 @@ To configure the LM of your choice, follow these steps:
```bash
make setup-config
```
This command will prompt you to enter the LLM API key and model name, ensuring that OpenDevin is tailored to your specific needs.
This command will prompt you to enter the LLM API key, model name, and other variables ensuring that OpenDevin is tailored to your specific needs. Note that the model name will apply only when you run headless. If you use the UI, please set the model in the UI.
**Note on Alternative Models:**
Some alternative models may prove more challenging to tame than others. Fear not, brave adventurer! We shall soon unveil LLM-specific documentation to guide you on your quest. And if you've already mastered the art of wielding a model other than OpenAI's GPT, we encourage you to [share your setup instructions with us](https://github.com/OpenDevin/OpenDevin/issues/417).
@@ -58,9 +72,25 @@ There is also [documentation for running with local models using ollama](./docs/
make start-frontend
```
### 6. Help
### 6. LLM Debugging
If you encounter any issues with the Language Model (LM) or you're simply curious, you can inspect the actual LLM prompts and responses. To do so, export DEBUG=1 in the environment and restart the backend. OpenDevin will then log the prompts and responses in the logs/llm/CURRENT_DATE directory, allowing you to identify the causes.
### 7. Help
- **Get Some Help:** Need assistance or information on available targets and commands? The help command provides all the necessary guidance to ensure a smooth experience with OpenDevin.
```bash
make help
```
### 8. Testing
#### Unit tests
```bash
poetry run pytest ./tests/unit/test_sandbox.py
```
#### Integration tests
Please refer to [this README](./tests/integration/README.md) for details.

122
Makefile
View File

@@ -1,3 +1,4 @@
SHELL=/bin/bash
# Makefile for OpenDevin project
# Variables
@@ -11,11 +12,11 @@ CONFIG_FILE = config.toml
PRECOMMIT_CONFIG_PATH = "./dev_config/python/.pre-commit-config.yaml"
# ANSI color codes
GREEN=\033[0;32m
YELLOW=\033[0;33m
RED=\033[0;31m
BLUE=\033[0;34m
RESET=\033[0m
GREEN=$(shell tput -Txterm setaf 2)
YELLOW=$(shell tput -Txterm setaf 3)
RED=$(shell tput -Txterm setaf 1)
BLUE=$(shell tput -Txterm setaf 6)
RESET=$(shell tput -Txterm sgr0)
# Build
build:
@@ -30,18 +31,33 @@ build:
check-dependencies:
@echo "$(YELLOW)Checking dependencies...$(RESET)"
@$(MAKE) -s check-system
@$(MAKE) -s check-python
@$(MAKE) -s check-npm
@$(MAKE) -s check-nodejs
@$(MAKE) -s check-docker
@$(MAKE) -s check-poetry
@echo "$(GREEN)Dependencies checked successfully.$(RESET)"
check-system:
@echo "$(YELLOW)Checking system...$(RESET)"
@if [ "$(shell uname)" = "Darwin" ]; then \
echo "$(BLUE)macOS detected.$(RESET)"; \
elif [ "$(shell uname)" = "Linux" ]; then \
echo "$(BLUE)Linux detected.$(RESET)"; \
elif [ "$$(uname -r | grep -i microsoft)" ]; then \
echo "$(BLUE)Windows Subsystem for Linux detected.$(RESET)"; \
else \
echo "$(RED)Unsupported system detected. Please use macOS, Linux, or Windows Subsystem for Linux (WSL).$(RESET)"; \
exit 1; \
fi
check-python:
@echo "$(YELLOW)Checking Python installation...$(RESET)"
@if command -v python3 > /dev/null; then \
echo "$(BLUE)$(shell python3 --version) is already installed.$(RESET)"; \
@if command -v python3.11 > /dev/null; then \
echo "$(BLUE)$(shell python3.11 --version) is already installed.$(RESET)"; \
else \
echo "$(RED)Python 3 is not installed. Please install Python 3 to continue.$(RESET)"; \
echo "$(RED)Python 3.11 is not installed. Please install Python 3.11 to continue.$(RESET)"; \
exit 1; \
fi
@@ -54,6 +70,22 @@ check-npm:
exit 1; \
fi
check-nodejs:
@echo "$(YELLOW)Checking Node.js installation...$(RESET)"
@if command -v node > /dev/null; then \
NODE_VERSION=$(shell node --version | sed -E 's/v//g'); \
IFS='.' read -r -a NODE_VERSION_ARRAY <<< "$$NODE_VERSION"; \
if [ "$${NODE_VERSION_ARRAY[0]}" -gt 18 ] || ([ "$${NODE_VERSION_ARRAY[0]}" -eq 18 ] && [ "$${NODE_VERSION_ARRAY[1]}" -gt 17 ]) || ([ "$${NODE_VERSION_ARRAY[0]}" -eq 18 ] && [ "$${NODE_VERSION_ARRAY[1]}" -eq 17 ] && [ "$${NODE_VERSION_ARRAY[2]}" -ge 1 ]); then \
echo "$(BLUE)Node.js $$NODE_VERSION is already installed.$(RESET)"; \
else \
echo "$(RED)Node.js 18.17.1 or later is required. Please install Node.js 18.17.1 or later to continue.$(RESET)"; \
exit 1; \
fi; \
else \
echo "$(RED)Node.js is not installed. Please install Node.js to continue.$(RESET)"; \
exit 1; \
fi
check-docker:
@echo "$(YELLOW)Checking Docker installation...$(RESET)"
@if command -v docker > /dev/null; then \
@@ -66,10 +98,19 @@ check-docker:
check-poetry:
@echo "$(YELLOW)Checking Poetry installation...$(RESET)"
@if command -v poetry > /dev/null; then \
echo "$(BLUE)$(shell poetry --version) is already installed.$(RESET)"; \
POETRY_VERSION=$(shell poetry --version 2>&1 | sed -E 's/Poetry \(version ([0-9]+\.[0-9]+\.[0-9]+)\)/\1/'); \
IFS='.' read -r -a POETRY_VERSION_ARRAY <<< "$$POETRY_VERSION"; \
if [ $${POETRY_VERSION_ARRAY[0]} -ge 1 ] && [ $${POETRY_VERSION_ARRAY[1]} -ge 8 ]; then \
echo "$(BLUE)$(shell poetry --version) is already installed.$(RESET)"; \
else \
echo "$(RED)Poetry 1.8 or later is required. You can install poetry by running the following command, then adding Poetry to your PATH:"; \
echo "$(RED) curl -sSL https://install.python-poetry.org | python3 -$(RESET)"; \
echo "$(RED)More detail here: https://python-poetry.org/docs/#installing-with-the-official-installer$(RESET)"; \
exit 1; \
fi; \
else \
echo "$(RED)Poetry is not installed. You can install poetry by running the following command, then adding Poetry to your PATH:"; \
echo "$(RED) curl -sSL https://install.python-poetry.org | python3 -$(RESET)"; \
echo "$(RED) curl -sSL https://install.python-poetry.org | python3.11 -$(RESET)"; \
echo "$(RED)More detail here: https://python-poetry.org/docs/#installing-with-the-official-installer$(RESET)"; \
exit 1; \
fi
@@ -87,6 +128,7 @@ install-python-dependencies:
poetry run pip install chroma-hnswlib; \
fi
@poetry install --without evaluation
@poetry run playwright install --with-deps chromium
@echo "$(GREEN)Python dependencies installed successfully.$(RESET)"
install-frontend-dependencies:
@@ -106,6 +148,25 @@ install-precommit-hooks:
@poetry run pre-commit install --config $(PRECOMMIT_CONFIG_PATH)
@echo "$(GREEN)Pre-commit hooks installed successfully.$(RESET)"
lint-backend:
@echo "$(YELLOW)Running linters...$(RESET)"
@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)"
@cd frontend && npm run build
@@ -113,12 +174,12 @@ build-frontend:
# Start backend
start-backend:
@echo "$(YELLOW)Starting backend...$(RESET)"
@poetry run uvicorn opendevin.server.listen:app --port $(BACKEND_PORT)
@poetry run uvicorn opendevin.server.listen:app --port $(BACKEND_PORT) --reload --reload-exclude "workspace/*"
# Start frontend
start-frontend:
@echo "$(YELLOW)Starting frontend...$(RESET)"
@cd frontend && BACKEND_HOST=$(BACKEND_HOST) FRONTEND_PORT=$(FRONTEND_PORT) npm run start
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run start
# Run the app
run:
@@ -144,7 +205,7 @@ setup-config:
@echo "$(GREEN)Config.toml setup completed.$(RESET)"
setup-config-prompts:
@read -p "Enter your LLM Model name (see https://docs.litellm.ai/docs/providers for full list) [default: $(DEFAULT_MODEL)]: " llm_model; \
@read -p "Enter your LLM Model name, used for running without UI. Set the model in the UI after you start the app. (see https://docs.litellm.ai/docs/providers for full list) [default: $(DEFAULT_MODEL)]: " llm_model; \
llm_model=$${llm_model:-$(DEFAULT_MODEL)}; \
echo "LLM_MODEL=\"$$llm_model\"" > $(CONFIG_FILE).tmp
@@ -154,17 +215,27 @@ setup-config-prompts:
@read -p "Enter your LLM Base URL [mostly used for local LLMs, leave blank if not needed - example: http://localhost:5001/v1/]: " llm_base_url; \
if [[ ! -z "$$llm_base_url" ]]; then echo "LLM_BASE_URL=\"$$llm_base_url\"" >> $(CONFIG_FILE).tmp; fi
@echo "Enter your LLM Embedding Model\nChoices are openai, azureopenai, llama2 or leave blank to default to 'BAAI/bge-small-en-v1.5' via huggingface"; \
read -p "> " llm_embedding_model; \
echo "LLM_EMBEDDING_MODEL=\"$$llm_embedding_model\"" >> $(CONFIG_FILE).tmp; \
if [ "$$llm_embedding_model" = "llama2" ]; then \
read -p "Enter the local model URL (will overwrite LLM_BASE_URL): " llm_base_url; \
echo "LLM_BASE_URL=\"$$llm_base_url\"" >> $(CONFIG_FILE).tmp; \
@echo "Enter your LLM Embedding Model"; \
echo "Choices are:"; \
echo " - openai"; \
echo " - azureopenai"; \
echo " - Embeddings available only with OllamaEmbedding:"; \
echo " - llama2"; \
echo " - mxbai-embed-large"; \
echo " - nomic-embed-text"; \
echo " - all-minilm"; \
echo " - stable-code"; \
echo " - Leave blank to default to 'BAAI/bge-small-en-v1.5' via huggingface"; \
read -p "> " llm_embedding_model; \
echo "LLM_EMBEDDING_MODEL=\"$$llm_embedding_model\"" >> $(CONFIG_FILE).tmp; \
if [ "$$llm_embedding_model" = "llama2" ] || [ "$$llm_embedding_model" = "mxbai-embed-large" ] || [ "$$llm_embedding_model" = "nomic-embed-text" ] || [ "$$llm_embedding_model" = "all-minilm" ] || [ "$$llm_embedding_model" = "stable-code" ]; then \
read -p "Enter the local model URL for the embedding model (will set LLM_EMBEDDING_BASE_URL): " llm_embedding_base_url; \
echo "LLM_EMBEDDING_BASE_URL=\"$$llm_embedding_base_url\"" >> $(CONFIG_FILE).tmp; \
elif [ "$$llm_embedding_model" = "azureopenai" ]; then \
read -p "Enter the Azure endpoint URL (will overwrite LLM_BASE_URL): " llm_base_url; \
echo "LLM_BASE_URL=\"$$llm_base_url\"" >> $(CONFIG_FILE).tmp; \
read -p "Enter the Azure LLM Deployment Name: " llm_deployment_name; \
echo "LLM_DEPLOYMENT_NAME=\"$$llm_deployment_name\"" >> $(CONFIG_FILE).tmp; \
read -p "Enter the Azure LLM Embedding Deployment Name: " llm_embedding_deployment_name; \
echo "LLM_EMBEDDING_DEPLOYMENT_NAME=\"$$llm_embedding_deployment_name\"" >> $(CONFIG_FILE).tmp; \
read -p "Enter the Azure API Version: " llm_api_version; \
echo "LLM_API_VERSION=\"$$llm_api_version\"" >> $(CONFIG_FILE).tmp; \
fi
@@ -173,11 +244,18 @@ setup-config-prompts:
workspace_dir=$${workspace_dir:-$(DEFAULT_WORKSPACE_DIR)}; \
echo "WORKSPACE_BASE=\"$$workspace_dir\"" >> $(CONFIG_FILE).tmp
# Clean up all caches
clean:
@echo "$(YELLOW)Cleaning up caches...$(RESET)"
@rm -rf opendevin/.cache
@echo "$(GREEN)Caches cleaned up successfully.$(RESET)"
# Help
help:
@echo "$(BLUE)Usage: make [target]$(RESET)"
@echo "Targets:"
@echo " $(GREEN)build$(RESET) - Build project, including environment setup and dependencies."
@echo " $(GREEN)lint$(RESET) - Run linters on the project."
@echo " $(GREEN)setup-config$(RESET) - Setup the configuration for OpenDevin by providing LLM API key,"
@echo " LLM Model name, and workspace directory."
@echo " $(GREEN)start-backend$(RESET) - Start the backend server for the OpenDevin project."
@@ -187,4 +265,4 @@ help:
@echo " $(GREEN)help$(RESET) - Display this help message, providing information on available targets."
# Phony targets
.PHONY: build check-dependencies check-python check-npm check-docker check-poetry pull-docker-image install-python-dependencies install-frontend-dependencies install-precommit-hooks start-backend start-frontend run setup-config setup-config-prompts help
.PHONY: build check-dependencies check-python check-npm check-docker check-poetry pull-docker-image install-python-dependencies install-frontend-dependencies install-precommit-hooks lint start-backend start-frontend run setup-config setup-config-prompts help

159
README.md
View File

@@ -1,4 +1,5 @@
<a name="readme-top"></a>
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
@@ -7,8 +8,6 @@
*** Thanks again! Now go create something AMAZING! :D
-->
<!-- PROJECT SHIELDS -->
<!--
*** I'm using markdown "reference style" links for readability.
@@ -25,156 +24,22 @@
<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="./logo.png" alt="Logo" width="200" height="200">
<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>
<!-- TABLE OF CONTENTS -->
<details>
<summary>🗂️ Table of Contents</summary>
<ol>
<li><a href="#-mission">🎯 Mission</a></li>
<li><a href="#-what-is-devin">🤔 What is Devin?</a></li>
<li><a href="#-why-opendevin">🐚 Why OpenDevin?</a></li>
<li><a href="#-project-status">🚧 Project Status</a></li>
<a href="#-get-started">🚀 Get Started</a>
<ul>
<li><a href="#1-requirements">1. Requirements</a></li>
<li><a href="#2-build-and-setup">2. Build and Setup</a></li>
<li><a href="#3-run-the-application">3. Run the Application</a></li>
<li><a href="#4-individual-server-startup">4. Individual Server Startup</a></li>
<li><a href="#5-help">5. Help</a></li>
</ul>
</li>
<li><a href="#%EF%B8%8F-research-strategy">⭐️ Research Strategy</a></li>
<li><a href="#-how-to-contribute">🤝 How to Contribute</a></li>
<li><a href="#-join-our-community">🤖 Join Our Community</a></li>
<li><a href="#%EF%B8%8F-built-with">🛠️ Built With</a></li>
<li><a href="#-license">📜 License</a></li>
</ol>
</details>
## 🎯 Mission
[Project Demo Video](https://github.com/OpenDevin/OpenDevin/assets/38853559/71a472cc-df34-430c-8b1d-4d7286c807c9)
Welcome to OpenDevin, an open-source project aiming to replicate Devin, an autonomous AI software engineer who is capable of executing complex engineering tasks and collaborating actively with users on software development projects. This project aspires to replicate, enhance, and innovate upon Devin through the power of the open-source community.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🤔 What is Devin?
Devin represents a cutting-edge autonomous agent designed to navigate the complexities of software engineering. It leverages a combination of tools such as a shell, code editor, and web browser, showcasing the untapped potential of LLMs in software development. Our goal is to explore and expand upon Devin's capabilities, identifying both its strengths and areas for improvement, to guide the progress of open code models.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🐚 Why OpenDevin?
The OpenDevin project is born out of a desire to replicate, enhance, and innovate beyond the original Devin model. By engaging the open-source community, we aim to tackle the challenges faced by Code LLMs in practical scenarios, producing works that significantly contribute to the community and pave the way for future advancements.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## 🚧 Project Status
OpenDevin is currently a work in progress, but you can already run the alpha version to see the end-to-end system in action. The project team is actively working on the following key milestones:
- **UI**: Developing a user-friendly interface, including a chat interface, a shell demonstrating commands, and a web browser.
- **Architecture**: Building a stable agent framework with a robust backend that can read, write, and run simple commands.
- **Agent Capabilities**: Enhancing the agent's abilities to generate bash scripts, run tests, and perform other software engineering tasks.
- **Evaluation**: Establishing a minimal evaluation pipeline that is consistent with Devin's evaluation criteria.
After completing the MVP, the team will focus on research in various areas, including foundation models, specialist capabilities, evaluation, and agent studies.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>
## ⚠️ Caveats and Warnings
* OpenDevin is still an alpha project. It is changing very quickly and is unstable. We are working on getting a stable release out in the coming weeks.
* OpenDevin will issue many prompts to the LLM you configure. Most of these LLMs cost money--be sure to set spending limits and monitor usage.
* OpenDevin runs `bash` commands within a Docker sandbox, so it should not affect your machine. But your workspace directory will be attached to that sandbox, and files in the directory may be modified or deleted.
* Our default Agent is currently the MonologueAgent, which has limited capabilities, but is fairly stable. We're working on other Agent implementations, including [SWE Agent](https://swe-agent.com/). You can [read about our current set of agents here](./docs/documentation/Agents.md).
## 🚀 Get Started
Getting started with the OpenDevin project is incredibly easy. Follow these simple steps to set up and run OpenDevin on your system:
The easiest way to run OpenDevin is inside a Docker container.
You can run:
```bash
# Your OpenAI API key, or any other LLM API key
export LLM_API_KEY="sk-..."
# The directory you want OpenDevin to modify. MUST be an absolute path!
export WORKSPACE_DIR=$(pwd)/workspace
docker run \
-e LLM_API_KEY \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_DIR \
-v $WORKSPACE_DIR:/opt/workspace_base \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 3000:3000 \
ghcr.io/opendevin/opendevin:latest
```
Replace `$(pwd)/workspace` with the path to the code you want OpenDevin to work with.
You can find opendevin running at `http://localhost:3000`.
See [Development.md](Development.md) for instructions on running OpenDevin without Docker.
## 🤖 LLM Backends
OpenDevin can work with any LLM backend.
For a full list of the LM providers and models available, please consult the
[litellm documentation](https://docs.litellm.ai/docs/providers).
The `LLM_MODEL` environment variable controls which model is used in programmatic interactions,
but choosing a model in the OpenDevin UI will override this setting.
The following environment variables might be necessary for some LLMs:
* `LLM_API_KEY`
* `LLM_BASE_URL`
* `LLM_EMBEDDING_MODEL`
* `LLM_DEPLOYMENT_NAME`
* `LLM_API_VERSION`
**Note on Alternative Models:**
Some alternative models may prove more challenging to tame than others.
Fear not, brave adventurer! We shall soon unveil LLM-specific documentation to guide you on your quest.
And if you've already mastered the art of wielding a model other than OpenAI's GPT,
we encourage you to [share your setup instructions with us](https://github.com/OpenDevin/OpenDevin/issues/417).
There is also [documentation for running with local models using ollama](./docs/documentation/LOCAL_LLM_GUIDE.md).
## ⭐️ Research Strategy
Achieving full replication of production-grade applications with LLMs is a complex endeavor. Our strategy involves:
1. **Core Technical Research:** Focusing on foundational research to understand and improve the technical aspects of code generation and handling.
2. **Specialist Abilities:** Enhancing the effectiveness of core components through data curation, training methods, and more.
3. **Task Planning:** Developing capabilities for bug detection, codebase management, and optimization.
4. **Evaluation:** Establishing comprehensive evaluation metrics to better understand and improve our models.
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;">
@@ -202,8 +67,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!
@@ -211,14 +76,6 @@ If you would love to contribute, feel free to join our community (note that now
[![Star History Chart](https://api.star-history.com/svg?repos=OpenDevin/OpenDevin&type=Date)](https://star-history.com/#OpenDevin/OpenDevin&Date)
## 🛠️ Built With
OpenDevin is built using a combination of powerful frameworks and libraries, providing a robust foundation for its development. Here are the key technologies used in the project:
![FastAPI](https://img.shields.io/badge/FastAPI-black?style=for-the-badge) ![uvicorn](https://img.shields.io/badge/uvicorn-black?style=for-the-badge) ![LiteLLM](https://img.shields.io/badge/LiteLLM-black?style=for-the-badge) ![Docker](https://img.shields.io/badge/Docker-black?style=for-the-badge) ![Ruff](https://img.shields.io/badge/Ruff-black?style=for-the-badge) ![MyPy](https://img.shields.io/badge/MyPy-black?style=for-the-badge) ![LlamaIndex](https://img.shields.io/badge/LlamaIndex-black?style=for-the-badge) ![React](https://img.shields.io/badge/React-black?style=for-the-badge)
Please note that the selection of these technologies is in progress, and additional technologies may be added or existing ones may be removed as the project evolves. We strive to adopt the most suitable and efficient tools to enhance the capabilities of OpenDevin.
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑

View File

@@ -26,6 +26,7 @@ The `state` contains:
Here is a list of available Actions, which can be returned by `agent.step()`:
- [`CmdRunAction`](../opendevin/action/bash.py) - Runs a command inside a sandboxed terminal
- [`CmdKillAction`](../opendevin/action/bash.py) - Kills a background command
- [`IPythonRunCellAction`](../opendevin/action/bash.py) - Execute a block of Python code interactively (in Jupyter notebook) and receives `CmdOutputObservation`. Requires setting up `jupyter` [plugin](../opendevin/sandbox/plugins) as a requirement.
- [`FileReadAction`](../opendevin/action/fileop.py) - Reads the content of a file
- [`FileWriteAction`](../opendevin/action/fileop.py) - Writes new content to a file
- [`BrowseURLAction`](../opendevin/action/browse.py) - Gets the content of a URL
@@ -33,6 +34,7 @@ Here is a list of available Actions, which can be returned by `agent.step()`:
- [`AddTaskAction`](../opendevin/action/tasks.py) - Adds a subtask to the plan
- [`ModifyTaskAction`](../opendevin/action/tasks.py) - Changes the state of a subtask
- [`AgentThinkAction`](../opendevin/action/agent.py) - A no-op that allows the agent to add plaintext to the history (as well as the chat log)
- [`AgentTalkAction`](../opendevin/action/agent.py) - A no-op that allows the agent to add plaintext to the history and talk to the user.
- [`AgentFinishAction`](../opendevin/action/agent.py) - Stops the control loop, allowing the user to enter a new task
You can use `action.to_dict()` and `action_from_dict` to serialize and deserialize actions.

View File

@@ -0,0 +1,405 @@
# GPT-4 Examples:
### Notes:
- The final result is not completed as I only gave it 10 steps, but shows very promising use of the tools.
- This whole run costs: $0.35 using gpt-4-turbo
- Input Tokens: 29,734
- Completion Tokens: 1,636
Interesting things:
1. Using comments as an initial placeholder for the next move and then having that output in memory is a very cool behavior.
2. It makes very small iterative changes due to the prompting.
3. A lot of the logic for the game is present but not put together well
4. There are a lot of odd quirks with the code that it has written.
5. Some odd agent behavior with putting random lines of code in random places?
6. The structure of the feedback loop needs some improvement so the agent can see the changes it has made better in context.
7. Might be beneficial to add in some line numbers so the model knows what lines it is looking at.
```
==============
STEP 0
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=91, prompt_tokens=2047, total_tokens=2138)
==== RAW OUTPUT ====
Thoughts:
To start implementing Conway's Game of Life in Python using the pygame library, I first need to set up a Python script file where I can write the code. I'll create a new Python file named `game_of_life.py`. This file will serve as the base for writing the game logic and using pygame functionalities.
Action:
"""
write game_of_life.py # This is a placeholder for the game of life implementation using pygame.
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='# This is a placeholder for the game of life implementation using pygame.', start=0, end=-1, thoughts="Thoughts:\nTo start implementing Conway's Game of Life in Python using the pygame library, I first need to set up a Python script file where I can write the code. I'll create a new Python file named `game_of_life.py`. This file will serve as the base for writing the game logic and using pygame functionalities.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 0:-1.
==============
STEP 1
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=101, prompt_tokens=2351, total_tokens=2452)
==== RAW OUTPUT ====
Thoughts:
The placeholder for the game of life implementation using pygame has been set up in the file `game_of_life.py`. The next step is to start implementing the basic structure of the game. This includes importing necessary libraries, initializing the pygame module, setting up the display window, and defining the main game loop. I will begin by importing pygame and initializing it, which are the foundational steps for any pygame application.
Action:
"""
edit 0 0 import pygame
pygame.init()
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='import pygame\n\npygame.init()', start=0, end=0, thoughts='Thoughts:\nThe placeholder for the game of life implementation using pygame has been set up in the file `game_of_life.py`. The next step is to start implementing the basic structure of the game. This includes importing necessary libraries, initializing the pygame module, setting up the display window, and defining the main game loop. I will begin by importing pygame and initializing it, which are the foundational steps for any pygame application.\n\nAction:', action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 0:0.
==============
STEP 2
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=123, prompt_tokens=2552, total_tokens=2675)
==== RAW OUTPUT ====
Thoughts:
Previously, I initialized pygame in the `game_of_life.py` file. The next step is to set up the display window for the game. This involves defining the size of the window and setting the title. This is crucial as it provides the visual interface for the game. I will also add a comment to indicate that this is the setup for the display window.
Action:
"""
edit 2 2
# Setup the display window
screen_size = (800, 600)
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("Conway's Game of Life")
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='# Setup the display window\nscreen_size = (800, 600)\nscreen = pygame.display.set_mode(screen_size)\npygame.display.set_caption("Conway\'s Game of Life")', start=2, end=2, thoughts='Thoughts:\nPreviously, I initialized pygame in the `game_of_life.py` file. The next step is to set up the display window for the game. This involves defining the size of the window and setting the title. This is crucial as it provides the visual interface for the game. I will also add a comment to indicate that this is the setup for the display window.\n\nAction:', action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 2:2.
==============
STEP 3
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=144, prompt_tokens=2777, total_tokens=2921)
==== RAW OUTPUT ====
Thoughts:
So far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame and setting up the display window. The next step is to define the main game loop where the game's logic will be continuously updated and rendered. This loop will handle events, update the game state, and redraw the screen. I will start by defining the main loop structure and a basic event handling mechanism to allow for quitting the game when the user presses the close button.
Action:
"""
edit 5 5 # Main game loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='# Main game loop\nrunning = True\nwhile running:\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n running = False', start=5, end=5, thoughts="Thoughts:\nSo far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame and setting up the display window. The next step is to define the main game loop where the game's logic will be continuously updated and rendered. This loop will handle events, update the game state, and redraw the screen. I will start by defining the main loop structure and a basic event handling mechanism to allow for quitting the game when the user presses the close button.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 5:5.
==============
STEP 4
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=222, prompt_tokens=3022, total_tokens=3244)
==== RAW OUTPUT ====
Thoughts:
So far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, and defining the main game loop. The next step is to implement the logic for Conway's Game of Life. This involves creating a grid to represent the game state, initializing it with a random pattern, and defining the rules for cell survival, birth, and death. I will start by defining the grid size and initializing the grid with random states.
Action:
"""
edit 7 7 # Define grid size and initialize grid
grid_size = (50, 50) # Grid size as a tuple (rows, columns)
grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])] # Initialize grid with zeros
import random
for y in range(grid_size[0]):
for x in range(grid_size[1]):
grid[y][x] = random.randint(0, 1) # Randomly assign cells as alive (1) or dead (0)
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='# Define grid size and initialize grid\ngrid_size = (50, 50) # Grid size as a tuple (rows, columns)\ngrid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])] # Initialize grid with zeros\nimport random\nfor y in range(grid_size[0]):\n for x in range(grid_size[1]):\n grid[y][x] = random.randint(0, 1) # Randomly assign cells as alive (1) or dead (0)', start=7, end=7, thoughts="Thoughts:\nSo far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, and defining the main game loop. The next step is to implement the logic for Conway's Game of Life. This involves creating a grid to represent the game state, initializing it with a random pattern, and defining the rules for cell survival, birth, and death. I will start by defining the grid size and initializing the grid with random states.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 7:7.
==============
STEP 5
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=234, prompt_tokens=3158, total_tokens=3392)
==== RAW OUTPUT ====
Thoughts:
So far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, and initializing the grid with random states. The next step is to implement the rules for Conway's Game of Life. This involves checking each cell's neighbors to determine its next state based on the number of alive neighbors. I will start by defining a function to count the alive neighbors for a given cell.
Action:
"""
edit 9 9 def count_alive_neighbors(grid, x, y):
directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
count = 0
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < grid_size[1] and 0 <= ny < grid_size[0]:
count += grid[ny][nx]
return count
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='def count_alive_neighbors(grid, x, y):\n directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]\n count = 0\n for dx, dy in directions:\n nx, ny = x + dx, y + dy\n if 0 <= nx < grid_size[1] and 0 <= ny < grid_size[0]:\n count += grid[ny][nx]\n return count', start=9, end=9, thoughts="Thoughts:\nSo far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, and initializing the grid with random states. The next step is to implement the rules for Conway's Game of Life. This involves checking each cell's neighbors to determine its next state based on the number of alive neighbors. I will start by defining a function to count the alive neighbors for a given cell.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 9:9.
==============
STEP 6
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=257, prompt_tokens=3290, total_tokens=3547)
==== RAW OUTPUT ====
Thoughts:
So far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating a function to count alive neighbors. The next step is to implement the rules for Conway's Game of Life. This involves updating the grid state based on the number of alive neighbors each cell has. I will define a function `update_grid` that applies the rules of the game to update the grid for the next generation.
Action:
"""
write game_of_life.py def update_grid(grid):
new_grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])]
for y in range(grid_size[0]):
for x in range(grid_size[1]):
alive_neighbors = count_alive_neighbors(grid, x, y)
if grid[y][x] == 1 and (alive_neighbors == 2 or alive_neighbors == 3):
new_grid[y][x] = 1
elif grid[y][x] == 0 and alive_neighbors == 3:
new_grid[y][x] = 1
return new_grid
11
11
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='def update_grid(grid):\n new_grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])]\n for y in range(grid_size[0]):\n for x in range(grid_size[1]):\n alive_neighbors = count_alive_neighbors(grid, x, y)\n if grid[y][x] == 1 and (alive_neighbors == 2 or alive_neighbors == 3):\n new_grid[y][x] = 1\n elif grid[y][x] == 0 and alive_neighbors == 3:\n new_grid[y][x] = 1\n return new_grid', start=11, end=11, thoughts="Thoughts:\nSo far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating a function to count alive neighbors. The next step is to implement the rules for Conway's Game of Life. This involves updating the grid state based on the number of alive neighbors each cell has. I will define a function `update_grid` that applies the rules of the game to update the grid for the next generation.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 11:11.
==============
STEP 7
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=124, prompt_tokens=3416, total_tokens=3540)
==== RAW OUTPUT ====
Thoughts:
So far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating functions to count alive neighbors and update the grid. The next step is to integrate the `update_grid` function into the main game loop to continuously update the state of the grid based on the game rules. This will allow the game to evolve over time, showing different generations of the cell population.
Action:
"""
edit 6 6
grid = update_grid(grid)
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='grid = update_grid(grid)', start=6, end=6, thoughts="Thoughts:\nSo far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating functions to count alive neighbors and update the grid. The next step is to integrate the `update_grid` function into the main game loop to continuously update the state of the grid based on the game rules. This will allow the game to evolve over time, showing different generations of the cell population.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 6:6.
==============
STEP 8
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=166, prompt_tokens=3393, total_tokens=3559)
==== RAW OUTPUT ====
Thoughts:
So far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating functions to count alive neighbors and update the grid. The next step is to integrate the `update_grid` function into the main game loop to continuously update the state of the grid based on the game rules. This will allow the game to evolve over time, showing different generations of the cell population. I need to ensure that the `update_grid` function is called within the main game loop and that the display is updated accordingly. I will start by checking the current content of the main game loop to see where to integrate the `update_grid` function.
Action:
"""
goto 6
"""
==== END RAW ====
ACTION:
FileReadAction(path='game_of_life.py', start=6, end=106, thoughts="Thoughts:\nSo far, I have set up the basic structure for Conway's Game of Life using pygame, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating functions to count alive neighbors and update the grid. The next step is to integrate the `update_grid` function into the main game loop to continuously update the state of the grid based on the game rules. This will allow the game to evolve over time, showing different generations of the cell population. I need to ensure that the `update_grid` function is called within the main game loop and that the display is updated accordingly. I will start by checking the current content of the main game loop to see where to integrate the `update_grid` function.\n\nAction:", action=<ActionType.READ: 'read'>)
OBSERVATION:
grid = update_grid(grid)
running = True
# Define grid size and initialize grid
grid_size = (50, 50) # Grid size as a tuple (rows, columns)
def count_alive_neighbors(grid, x, y):
directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
def update_grid(grid):
new_grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])]
for y in range(grid_size[0]):
for x in range(grid_size[1]):
alive_neighbors = count_alive_neighbors(grid, x, y)
if grid[y][x] == 1 and (alive_neighbors == 2 or alive_neighbors == 3):
new_grid[y][x] = 1
elif grid[y][x] == 0 and alive_neighbors == 3:
new_grid[y][x] = 1
return new_grid
count = 0
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < grid_size[1] and 0 <= ny < grid_size[0]:
count += grid[ny][nx]
return count
grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])] # Initialize grid with zeros
import random
for y in range(grid_size[0]):
for x in range(grid_size[1]):
grid[y][x] = random.randint(0, 1) # Randomly assign cells as alive (1) or dead (0)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.set_caption("Conway's Game of Life")
pygame.init()
==============
STEP 9
PLAN:
Please make conway's game of life in python using the pygame library. Be sure to use the tools in your workspace to help you out.
Usage(completion_tokens=174, prompt_tokens=3728, total_tokens=3902)
==== RAW OUTPUT ====
Thoughts:
From the previous actions, it seems that the basic structure for Conway's Game of Life using pygame has been set up, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating functions to count alive neighbors and update the grid. The next step is to ensure that the game loop properly updates the grid and refreshes the display to show the new state of the grid in each iteration. This involves calling `pygame.display.update()` to update the entire screen and using `pygame.time.Clock()` to control the frame rate. I will start by adding these necessary updates to the game loop.
Action:
"""
edit 95 95
pygame.display.update()
clock = pygame.time.Clock()
clock.tick(10) # Control the frame rate (10 frames per second)
"""
==== END RAW ====
ACTION:
FileWriteAction(path='game_of_life.py', content='pygame.display.update()\n clock = pygame.time.Clock()\n clock.tick(10) # Control the frame rate (10 frames per second)', start=95, end=95, thoughts="Thoughts:\nFrom the previous actions, it seems that the basic structure for Conway's Game of Life using pygame has been set up, including initializing pygame, setting up the display window, defining the main game loop, initializing the grid with random states, and creating functions to count alive neighbors and update the grid. The next step is to ensure that the game loop properly updates the grid and refreshes the display to show the new state of the grid in each iteration. This involves calling `pygame.display.update()` to update the entire screen and using `pygame.time.Clock()` to control the frame rate. I will start by adding these necessary updates to the game loop.\n\nAction:", action=<ActionType.WRITE: 'write'>)
OBSERVATION:
WRITE OPERATION:
You have written to "game_of_life.py" on these lines: 95:95.
16:43:55 - opendevin:INFO: agent_controller.py:125 - Exited before finishing the task.
```
## Result:
```
import pygame
# Setup the display window
screen_size = (800, 600)
screen = pygame.display.set_mode(screen_size)
# Main game loop
grid = update_grid(grid)
running = True
# Define grid size and initialize grid
grid_size = (50, 50) # Grid size as a tuple (rows, columns)
def count_alive_neighbors(grid, x, y):
directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
def update_grid(grid):
new_grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])]
for y in range(grid_size[0]):
for x in range(grid_size[1]):
alive_neighbors = count_alive_neighbors(grid, x, y)
if grid[y][x] == 1 and (alive_neighbors == 2 or alive_neighbors == 3):
new_grid[y][x] = 1
elif grid[y][x] == 0 and alive_neighbors == 3:
new_grid[y][x] = 1
return new_grid
count = 0
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < grid_size[1] and 0 <= ny < grid_size[0]:
count += grid[ny][nx]
return count
grid = [[0 for x in range(grid_size[1])] for y in range(grid_size[0])] # Initialize grid with zeros
import random
for y in range(grid_size[0]):
for x in range(grid_size[1]):
grid[y][x] = random.randint(0, 1) # Randomly assign cells as alive (1) or dead (0)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.set_caption("Conway's Game of Life")
pygame.init()
# This is a placeholder for the game of life implementation using pygame.pygame.display.update()
clock = pygame.time.Clock()
clock.tick(10) # Control the frame rate (10 frames per second)
```

View File

@@ -0,0 +1,5 @@
from opendevin.controller.agent import Agent
from .agent import SWEAgent
Agent.register('SWEAgent', SWEAgent)

108
agenthub/SWE_agent/agent.py Normal file
View File

@@ -0,0 +1,108 @@
from typing import List
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Action,
AgentThinkAction,
FileReadAction,
FileWriteAction,
)
from opendevin.events.observation import Observation
from opendevin.llm.llm import LLM
from .parser import parse_command
from .prompts import (
CONTEXT_PROMPT,
MEMORY_FORMAT,
NO_ACTION,
STEP_PROMPT,
SYSTEM_MESSAGE,
)
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 'goto', 'search_for', 'edit', 'scroll', 'run'
"""
def __init__(self, llm: LLM):
super().__init__(llm)
self.memory_window = 4
self.max_retries = 2
self.running_memory: List[str] = []
self.cur_file: str = ''
self.cur_line: int = 0
def _remember(self, action: Action, observation: Observation) -> None:
"""Agent has a limited memory of the few steps implemented as a queue"""
memory = MEMORY_FORMAT(action.to_memory(), observation.to_memory())
self.running_memory.append(memory)
def _think_act(self, messages: List[dict]) -> tuple[Action, str]:
resp = self.llm.completion(
messages=messages,
temperature=0.05,
)
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',
)
return parse_command(action_resp, self.cur_file, self.cur_line)
def _update(self, action: Action) -> None:
if isinstance(action, (FileReadAction, FileWriteAction)):
self.cur_file = action.path
self.cur_line = action.start
def step(self, 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)
"""
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)
msgs = [
{'content': SYSTEM_MESSAGE, 'role': 'system'},
{'content': prompt, 'role': 'user'},
]
if len(self.running_memory) > 0:
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)]))
action, thought = self._think_act(messages=msgs)
start_msg_len = len(msgs)
while not action and len(msgs) < self.max_retries + start_msg_len:
error = NO_ACTION(thought)
error_msg = {'content': error, 'role': 'user'}
msgs.append(error_msg)
action, thought = self._think_act(messages=msgs)
if not action:
action = AgentThinkAction(thought)
self._update(action)
self.latest_action = action
return action
def search_memory(self, query: str) -> List[str]:
return [item for item in self.running_memory if query in item]
def reset(self) -> None:
"""Used to reset the agent"""
self.running_memory = []
super().reset()

View File

@@ -0,0 +1,164 @@
import re
from opendevin.events.action import (
Action,
AgentEchoAction,
AgentFinishAction,
AgentThinkAction,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
)
from .prompts import COMMAND_USAGE, CUSTOM_DOCS
# commands: exit, read, write, browse, kill, search_file, search_dir
no_open_file_error = AgentEchoAction(
'You are not currently in a file. You can use the read command to open a file and then use goto to navigate through it.')
def invalid_error(cmd, docs):
return f"""ERROR:
Invalid command structure for
```
{cmd}
```
You may have caused this error by having multiple commands within your command block.
If so, try again by running only one of the commands:
Try again using this format:
{COMMAND_USAGE[docs]}
"""
def get_action_from_string(command_string: str, path: str, line: int, thoughts: str = '') -> Action | None:
"""
Parses the command string to find which command the agent wants to run
Converts the command into a proper Action and returns
"""
vars = command_string.split(' ')
cmd = vars[0]
args = [] if len(vars) == 1 else vars[1:]
if 'exit' == cmd:
return AgentFinishAction()
elif 'think' == cmd:
return AgentThinkAction(' '.join(args))
elif 'scroll_up' == cmd:
if not path:
return no_open_file_error
return FileReadAction(path, line + 100, line + 200, thoughts)
elif 'scroll_down' == cmd:
if not path:
return no_open_file_error
return FileReadAction(path, line - 100, line, thoughts)
elif 'goto' == cmd:
if not path:
return no_open_file_error
rex = r'^goto\s+(\d+)$'
valid = re.match(rex, command_string)
if valid:
start = int(valid.group(1))
end = start + 100
return FileReadAction(path, start, end, thoughts)
else:
return AgentEchoAction(invalid_error(command_string, 'goto'))
elif 'edit' == cmd:
if not path:
return no_open_file_error
rex = r'^edit\s+(\d+)\s+(-?\d+)\s+(\S.*)$'
valid = re.match(rex, command_string, re.DOTALL)
if valid:
start = int(valid.group(1))
end = int(valid.group(2))
change = valid.group(3)
if '"' == change[-1] and '"' == change[0]:
change = change[1:-1]
return FileWriteAction(path, change, start, end, thoughts)
else:
return AgentEchoAction(invalid_error(command_string, 'edit'))
elif 'read' == cmd:
rex = r'^read\s+(\S+)(?:\s+(\d+))?(?:\s+(-?\d+))?$'
valid = re.match(rex, command_string, re.DOTALL)
if valid:
file = valid.group(1)
start_str = valid.group(2)
end_str = valid.group(3)
start = 0 if not start_str else int(start_str)
end = -1 if not end_str else int(end_str)
return FileReadAction(file, start, end, thoughts)
else:
return AgentEchoAction(invalid_error(command_string, 'read'))
elif 'write' == cmd:
rex = r'^write\s+(\S+)\s+(.*?)\s*(\d+)?\s*(-?\d+)?$'
valid = re.match(rex, command_string, re.DOTALL)
if valid:
file = valid.group(1)
content = valid.group(2)
start_str = valid.group(3)
end_str = valid.group(4)
start = 0 if not start_str else int(start_str)
end = -1 if not end_str else int(end_str)
if '"' == content[-1] and '"' == content[0]:
content = content[1:-1]
return FileWriteAction(file, content, start, end, thoughts)
else:
return AgentEchoAction(invalid_error(command_string, 'write'))
elif 'browse' == cmd:
return BrowseURLAction(args[0].strip())
elif cmd in ['search_file', 'search_dir', 'find_file']:
rex = r'^(search_file|search_dir|find_file)\s+(\S+)(?:\s+(\S+))?$'
valid = re.match(rex, command_string, re.DOTALL)
if valid:
return CmdRunAction(command_string)
else:
return AgentEchoAction(f'Invalid command structure for\n ```\n{command_string}\n```.\nTry again using this format:\n{CUSTOM_DOCS}')
else:
# check bash command
obs = str(CmdRunAction(f'type {cmd}'))
if obs.split(':')[-1].strip() == 'not found':
# echo not found error for llm
return AgentEchoAction(content=obs)
else:
# run valid command
return CmdRunAction(command_string)
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.
Args:
input_str (str): The input string to be parsed.
Returns:
tuple: A tuple containing the command and the accompanying text (if any).
"""
input_str = input_str.strip()
if '```' in input_str:
parts = input_str.split('```')
command_str = parts[1].strip()
ind = 2 if len(parts) > 2 else 1
accompanying_text = ''.join(parts[:-ind]).strip()
action = get_action_from_string(
command_str, path, line, accompanying_text)
if action:
return action, accompanying_text
return None, input_str # used for retry

View File

@@ -0,0 +1,206 @@
DEFAULT_COMMANDS_DICT = {
'exit': 'Executed when task is complete',
'read <file_name> [<start_line>] [<end_line>]': "Shows a given file's contents starting from <start_line> up to <end_line>. Default: start_line = 0, end_line = -1. By default the whole file will be read.",
'write <file> <changes> [<start_line>] [<end_line>]': 'Modifies a <file> by replacing the current lines between <start_line> and <end_line> with <changes>. Default start_line = 0 and end_line = -1. Calling this with no line args will replace the whole file.',
'browse <url>': 'Returns the text version of any url, this can be useful to look up documentation or finding issues on github',
'scroll_up': 'Takes no arguments. This will scroll up and show you the 100 lines above your current lines',
'scroll_down': 'Takes no arguments. This will scroll down and show you the 100 lines below your current lines',
'edit <start_line> <end_line> <changes>': 'This will modify lines in the currently open file. use start_line and end_line to designate which lines to change and then write the multiline changes',
'goto <line_num>': 'This will take you directly to a line and show you the 100 lines below it.',
'<bash_command> <args>': 'You can use any bash command you need (cd, ls, rm, grep, dir, mv, wget, git, zip, etc.) with their arguments included',
'pip install <package>': 'You can use this to import python packages. Make sure you include the correct package name when using this command.',
'ls': 'Use the ls command to view all the files in your current directory, this is a good starting point.',
'NOT ALLOWED': 'You cannot use interactive commands like python or node'
}
COMMAND_USAGE = {
'exit': 'Usage:\n```\nexit\n```\nExecuted when task is complete',
'read': "Args:\n<file_name> [<start_line>] [<end_line>]\nUsage:\n```\nread file.py\n```\nor\n```\nread example.py <start_line> <end_line>\n```\nShows a given file's contents starting from <start_line> up to <end_line>. Default: start_line = 0, end_line = -1. by default the whole file will be read.",
'write': 'Args:\n<file> <changes> [<start_line>] [<end_line>]\nUsage:\n```\nwrite "def main():\n print("This is line one")" 0 2\n```\nModifies a <file> by replacing the current lines between <start_line> and <end_line> with <changes>. Default start_line = 0 and end_line = -1. Calling this with no line args will replace the whole file.',
'edit': 'Args:\n<start_line> <end_line> <changes>\nUsage:\n```\nedit 0 1 import pandas as pd\n```\nThis will modify the current file you are in with the changes you make between the line numbers you designate',
'goto': 'Args:\n<line_num>\nUsage:\n```\ngoto <line_num>\n```\nThis will show you the 100 lines below and including the line you specify within your current file.',
'scroll_up': 'Usage:\n```\nscroll_up\n```\nThis will return the 100 lines above where you are currently at',
'scroll_down': 'Usage:\n```\nscroll_down\n```\nThis will return the 100 line below where you are currently at',
'browse': 'Args:\n<url>\nUsage:\n```\nbrowse https://github.com/OpenDevin/OpenDevin\n```\nThis will fetch the Text elements from the given url and show them to you.',
}
DEFAULT_COMMANDS = '\n'.join(
[k + ' - ' + v for k, v in DEFAULT_COMMANDS_DICT.items()])
# from opendevin.parse_commands import parse_command_file
# USE parse_command_file(filepath) to get the custom commands
CUSTOM_DOCS = None
CUSTOM_COMMANDS = f"""Custom bash commands:
{CUSTOM_DOCS}
"""
DOCUMENTATION = f"""DOCUMENTATION:
It is recommend that you use the commands provided for interacting with files and your directory because they have been specially built for you.
They will make it much easier for you to look at files and make changes. Using these commands will help you be better at your task.
You can open an file by using either the read or write operations.
- If a file already exists you should read it before making any changes. Use the `edit` command to make changes once you have read it.
- If you are creating a new file use the write command. Use the `edit` command to make changes once you have created the new file.
Commands:
{DEFAULT_COMMANDS}
{CUSTOM_COMMANDS}
The following commands require an open file to be used: edit, scroll_up, scroll_down, goto
To modify the current file use 'edit'. To move through the current file use 'goto' or 'scroll_up'/'scroll_down'
when using write and edit do not surround the code with any "" just write the code.
"""
GENERAL_GUIDELINES = """INSTRUCTIONS:
Now, you're going to solve this issue on your own. You can use any bash commands or custom commands you wish to complete your task. Edit all the files you need to and run any checks or tests that you want.
Remember, YOU CAN ONLY ENTER ONE COMMAND AT A TIME. You should always wait for feedback after every command.
When you're satisfied with all of the changes you've made, you can indicate that you are done by running the exit command.
Note however that you cannot use any interactive session commands (e.g. python, vim, node) in this environment, but you can write scripts and run them. E.g. you can write a python script and then run it with `python <script_name>.py`.
NOTE ABOUT THE write COMMAND: Indentation really matters! When editing a file, make sure to insert appropriate indentation before each line!
IMPORTANT TIPS:
1. Reproduce the bug: Always start by trying to replicate the bug that the issue discusses. If the issue includes code for reproducing the bug, we recommend that you re-implement that in your environment and run it to ensure you can reproduce the bug. Then, start trying to fix it. When you think you've fixed the bug, re-run the bug reproduction script to make sure that the issue has indeed been resolved.
If the bug reproduction script does not print anything when it successfully runs, we recommend adding a print("Script completed successfully, no errors.") command at the end of the file, so that you can be sure the script ran fine all the way through.
2. Try different commands: If you run a command and it doesn't work, try running a different command. A command that did not work once will not work the second time unless you modify it.
3. Navigate large files: If you open a file and need to get to an area around a specific line that is not in the first 100 lines, say line 583, you would use the 'read' command like this: 'read <file> 583'. This is a much faster way to read through the file.
4. Handle input files: If the bug reproduction script requires inputting/reading a specific file, such as 'buggy-input.png', and you'd like to understand how to input that file, conduct a search in the existing repository code to see whether someone else has already done that. Do this by running the command: 'search_dir "buggy-input.png"'. If that doesn't work, use the Linux 'find' command.
5. Understand your context: Always make sure to look at the currently open file and the current working directory. The currently open file might be in a different directory than the working directory.
6. Verify your edits: When editing files, it is easy to accidentally specify a wrong line number or to write code with incorrect indentation. Always check the code after you issue an edit to make sure that it reflects what you wanted to accomplish. If it didn't, issue another command to fix it.
7. Thoroughly test your solution: After making any changes to fix a bug, be sure to thoroughly test your solution to ensure the bug has been resolved. Re-run the bug reproduction script and verify that the issue has been addressed.
"""
RESPONSE_FORMAT = """RESPONSE FORMAT:
This is the format of the response you will make in order to solve the current issue.
You will be given multiple iterations to complete this task so break it into steps and solve them one by one.
Your output must contain the following:
- First, thoughts about what your next action should be and plan it out.
- You will have a memory of your thoughts so you can use this to remember things for the next step.
- Use your thoughts to think about what you are currently doing, what you have done on prior steps and how that relates to solving the problem.
- Second, create a piece of code that will execute your next action based on the thoughts you have.
- Remember that you can only have one action for each thought, do not include multiple actions.
Your code MUST be surrounded in triple back ticks EXACTLY like this:
```
<code>
```
Notes:
- Adhere to the format so that the program loop continues smoothly, it is very important to only give one command per output.
- DO NOT give more than one command within the triple backticks. This will just throw an error and nothing will happen as a result.
- Do not give multiple code blocks, if you do only the second one will be captured and run, this might give an error if the first one was necessary.
- To execute multiple commands you should write them down in your thoughts section so you can remember it on the next step and execute them then.
- The only commands you are not capable of executing are interactive commands like `python` or `node` by themselves.
- If you think that you have completed the task that has been given to you based on your previous actions and outputs then use ``` exit ``` as the command to let the system know that you are done.
- DO NOT make any copies of your previous memories those will be provided to you at each step, making copies just wastes time and energy. Think smarter not harder.
- The write and edit commands requires proper indentation in the content section ex. `write hw.py def hello():\n print(\'Hello World\')` this is how you would have to format your write command.
- The white spaces matter as the code changes will be added to the code so they must have proper syntax.
This is a template using the format described above
Items in <> are suggestions for you, fill them out based on the context of the problem you are solving.
[ FORMAT ]
Thoughts:
<Provide clear and concise thoughts on the next step to take, highlighting any important details or context that should be remembered.>
<You can use multiple lines to express your thoughts>
Action:
```
<command> <params>
```
[ END FORMAT ]
Do not provide anything extra just your thought and action.
"""
SYSTEM_MESSAGE = f"""SYSTEM INFO:
You am an autonomous coding agent, here to provide solutions for coding issues.
You have been designed to assist you with a wide range of programming tasks, from code editing and debugging to testing and deployment.
You have access to a variety of tools and commands that you can use to help you solve problems efficiently.
{GENERAL_GUIDELINES}
{DOCUMENTATION}
""".strip()
def NO_ACTION(latest): return f"""
You did not include any action to take in your most recent output:
===== Output ======
{latest}
==== End Output ===
Remember these are the custom commands you can use:
{DOCUMENTATION}
Lets try that again, it is very important that you adhere to the output format
This time, be sure to use the exact format below, replacing anything in <> with the appropriate value(s):
{RESPONSE_FORMAT}
It is crucial you use the format provided as the output will be parsed automatically.
"""
def file_info(file: str, line: int):
if file:
return f"""CURRENT WORKSPACE:
Open File: {file} on line {line}
You can use these commands with the current file:
Navigation: `scroll_up`, `scroll_down`, and `goto <line>`
Modification: `edit <start_line> <end_line> <changes>`
"""
def STEP_PROMPT(task, file, line_num): return f"""
{RESPONSE_FORMAT}
You are currently trying to complete this task:
{task}
{file_info(file, line_num)}
Keep all of the guidelines above in mind when you are thinking and making code.
Please come up with a thought and action based on your current task and latest steps.
Make sure that you do not repeat the same actions, there will not be any changes in result if you do not changes anything.
Be very strict about the formatting that you use and make sure you follow the guidelines.
NEVER output multiple commands. ONLY take ONE STEP at a time.
When you have completed your task run the "exit" command.
Begin with your thought about the next step and then come up with an action to perform your thought.
""".strip()
def unpack_dict(data: dict, restrict: list[str] = []):
lines = []
for key, value in data.items():
if key in restrict:
continue
elif isinstance(value, dict):
nested_str = unpack_dict(value, restrict).replace('\n', '\n ')
val = f'{key}:' + '\n ' + f'{nested_str}'
lines.append(val)
else:
lines.append(f'{key}: {value}')
return '\n'.join(lines)
def MEMORY_FORMAT(act, obs): return f"""
Previous Action:
{unpack_dict(act, ["content"])}
Output from Action:
{unpack_dict(obs)}
""".strip()
def CONTEXT_PROMPT(memory, window):
res = f'These are your past {window} actions:\n'
window_size = window if len(memory) > window else len(memory)
cur_mems = memory[-window_size:]
res += '===== Previous Actions =====\n'
for idx, mem in enumerate(cur_mems):
res += f'\nMemory {idx}:\n{mem}\n'
res += '======= End Actions =======\n'
res += 'Use these memories to provide additional context to the problem you are solving.\nRemember that you have already completed these steps so you do not need to perform them again.'
return res

View File

@@ -1,9 +1,42 @@
from dotenv import load_dotenv
from opendevin.controller.agent import Agent
from .micro.agent import MicroAgent
from .micro.registry import all_microagents
load_dotenv()
# Import agents after environment variables are loaded
from . import monologue_agent # noqa: E402
from . import codeact_agent # noqa: E402
from . import planner_agent # noqa: E402
__all__ = ['monologue_agent', 'codeact_agent', 'planner_agent']
from . import ( # noqa: E402
SWE_agent,
codeact_agent,
delegator_agent,
dummy_agent,
monologue_agent,
planner_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,
},
)
Agent.register(name, anon_class)

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

View File

@@ -1,4 +1,5 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .codeact_agent import CodeActAgent
Agent.register('CodeActAgent', CodeActAgent)

View File

@@ -1,74 +1,109 @@
import re
from typing import List, Mapping
from opendevin.action import (
from agenthub.codeact_agent.prompt import EXAMPLES, SYSTEM_MESSAGE
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Action,
AgentEchoAction,
AgentFinishAction,
AgentTalkAction,
CmdRunAction,
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.parse_commands import parse_command_file
from opendevin.state import State
COMMAND_DOCS = parse_command_file()
COMMAND_SEGMENT = (
f"""
Apart from the standard bash commands, you can also use the following special commands:
{COMMAND_DOCS}
"""
if COMMAND_DOCS is not None
else ''
)
SYSTEM_MESSAGE = f"""You are a helpful assistant. You will be provided access (as root) to a bash shell to complete user-provided tasks.
You will be able to execute commands in the bash shell, interact with the file system, install packages, and receive the output of your commands.
DO NOT provide code in ```triple backticks```. Instead, you should execute bash command on behalf of the user by wrapping them with <execute> and </execute>.
For example:
You can list the files in the current directory by executing the following command:
<execute>ls</execute>
You can also install packages using pip:
<execute> pip install numpy </execute>
You can also write a block of code to a file:
<execute>
echo "import math
print(math.pi)" > math.py
</execute>
{COMMAND_SEGMENT}
When you are done, execute the following to close the shell and end the conversation:
<execute>exit</execute>
"""
INVALID_INPUT_MESSAGE = (
"I don't understand your input. \n"
'If you want to execute command, please use <execute> YOUR_COMMAND_HERE </execute>.\n'
'If you already completed the task, please exit the shell by generating: <execute> exit </execute>.'
from opendevin.llm.llm import LLM
from opendevin.runtime.plugins import (
JupyterRequirement,
PluginRequirement,
SWEAgentCommandsRequirement,
)
def parse_response(response) -> str:
action = response.choices[0].message.content
if '<execute>' in action and '</execute>' not in action:
action += '</execute>'
for lang in ['bash', 'ipython']:
if f'<execute_{lang}>' in action and f'</execute_{lang}>' not in action:
action += f'</execute_{lang}>'
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(),
]
SUPPORTED_ACTIONS = (
CmdRunAction,
IPythonRunCellAction,
AgentEchoAction,
AgentTalkAction,
NullAction,
)
SUPPORTED_OBSERVATIONS = (
AgentMessageObservation,
UserMessageObservation,
CmdOutputObservation,
IPythonRunCellObservation,
)
def __init__(
self,
llm: LLM,
@@ -84,74 +119,106 @@ 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:
assert state.plan.main_goal, 'Expecting instruction to be set'
self.messages = [
{'role': 'system', 'content': SYSTEM_MESSAGE},
{'role': 'user', 'content': state.plan.main_goal},
{
'role': 'user',
'content': (
f'Here is an example of how you can interact with the environment for task solving:\n{EXAMPLES}\n\n'
f"NOW, LET'S START!\n\n{state.plan.main_goal}"
),
},
]
updated_info = state.updated_info
if updated_info:
for prev_action, obs in updated_info:
assert isinstance(
prev_action, (CmdRunAction, AgentEchoAction)
), 'Expecting CmdRunAction or AgentEchoAction for Action'
if isinstance(
obs, AgentMessageObservation
): # warning message from itself
self.messages.append(
{'role': 'user', 'content': obs.content})
prev_action, self.SUPPORTED_ACTIONS
), f'{prev_action.__class__} is not supported (supported: {self.SUPPORTED_ACTIONS})'
# prev_action is already added to self.messages when returned
# handle observations
assert isinstance(
obs, self.SUPPORTED_OBSERVATIONS
), f'{obs.__class__} is not supported (supported: {self.SUPPORTED_OBSERVATIONS})'
if isinstance(obs, (AgentMessageObservation, UserMessageObservation)):
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'
)
content = '\n'.join(splited)
content = truncate_observation(content)
self.messages.append({'role': 'user', 'content': content})
else:
raise NotImplementedError(
f'Unknown observation type: {obs.__class__}'
)
response = self.llm.completion(
messages=self.messages,
stop=['</execute>'],
temperature=0.0
stop=[
'</execute_ipython>',
'</execute_bash>',
],
temperature=0.0,
)
action_str: str = parse_response(response)
state.num_of_chars += sum(len(message['content'])
for message in self.messages) + len(action_str)
state.num_of_chars += sum(
len(message['content']) for message in self.messages
) + len(action_str)
self.messages.append({'role': 'assistant', 'content': action_str})
command = re.search(r'<execute>(.*)</execute>', action_str, re.DOTALL)
if command is not None:
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
command_group = command.group(1)
command_group = bash_command.group(1).strip()
if command_group.strip() == 'exit':
return AgentFinishAction()
return CmdRunAction(command=command_group)
# # execute the code
# # TODO: does exit_code get loaded into Message?
# exit_code, observation = self.env.execute(command_group)
# self._history.append(Message(Role.ASSISTANT, observation))
return CmdRunAction(command=command_group, thought=thought)
elif python_code := re.search(
r'<execute_ipython>(.*)</execute_ipython>', action_str, re.DOTALL
):
# a code block was found
code_group = python_code.group(1).strip()
thought = action_str.replace(python_code.group(0), '').strip()
return IPythonRunCellAction(code=code_group, thought=thought)
else:
# we could provide a error message for the model to continue similar to
# https://github.com/xingyaoww/mint-bench/blob/main/mint/envs/general_env.py#L18-L23
# observation = INVALID_INPUT_MESSAGE
# self._history.append(Message(Role.ASSISTANT, observation))
return AgentEchoAction(
content=INVALID_INPUT_MESSAGE
) # warning message to itself
# We assume the LLM is GOOD enough that when it returns pure natural language
# it want to talk to the user
return AgentTalkAction(content=action_str)
def search_memory(self, query: str) -> List[str]:
raise NotImplementedError('Implement this abstract method')

View File

@@ -0,0 +1,226 @@
from opendevin.runtime.plugins import SWEAgentCommandsRequirement
_SWEAGENT_BASH_DOCS = '\n'.join(
filter(
lambda x: not x.startswith('submit'),
SWEAgentCommandsRequirement.documentation.split('\n'),
)
)
# _SWEAGENT_BASH_DOCS content below:
"""
open <path> [<line_number>] - opens the file at the given path in the editor. If line_number is provided, the window will be move to include that line
goto <line_number> - moves the window to show <line_number>
scroll_down - moves the window down {WINDOW} lines
scroll_up - moves the window down {WINDOW} lines
create <filename> - creates and opens a new file with the given name
search_dir <search_term> [<dir>] - searches for search_term in all files in dir. If dir is not provided, searches in the current directory
search_file <search_term> [<file>] - searches for search_term in file. If file is not provided, searches in the current open file
find_file <file_name> [<dir>] - finds all files with the given name in dir. If dir is not provided, searches in the current directory
edit <start_line>:<end_line>
<replacement_text>
end_of_edit - replaces lines <start_line> through <end_line> (inclusive) with the given text in the open file. The replacement text is terminated by a line with only end_of_edit on it. All of the <replacement text> will be entered, so make sure your indentation is formatted properly. Python files will be checked for syntax errors after the edit. If the system detects a syntax error, the edit will not be executed. Simply try to edit the file again, but make sure to read the error message and modify the edit command you issue accordingly. Issuing the same command a second time will just lead to the same error message again.
"""
_COMMAND_DOCS = (
'\nApart from the standard bash commands, you can also use the following special commands in <execute_bash> environment:\n'
f'{_SWEAGENT_BASH_DOCS}'
"Please note that THE EDIT COMMAND REQUIRES PROPER INDENTATION. If you'd like to add the line ' print(x)' you must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run."
)
SYSTEM_MESSAGE = f"""A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.
The assistant can interact with an interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code should be enclosed using "<execute_ipython>" tag, for example:
<execute_ipython>
print("Hello World!")
</execute_ipython>
The assistant can execute bash commands on behalf of the user by wrapping them with <execute_bash> and </execute_bash>.
For example, you can list the files in the current directory by <execute_bash> ls </execute_bash>.
The assistant should attempt fewer things at a time instead of putting too much commands OR code in one "execute" block.
The assistant can install Python packages through bash by <execute_bash> pip install [package needed] </execute_bash> and should always import packages and define variables before starting to use them.
The assistant should stop <execute> and provide an answer when they have already obtained the answer from the execution result.
{_COMMAND_DOCS}
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 = """
--- START OF EXAMPLE ---
USER: Can you create a list of numbers from 1 to 10, and create a web page to display them at port 5000?
ASSISTANT:
Sure! Let me write the Python code for starting a web server and save it to a file `app.py`:
<execute_ipython>
CODE='''
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
numbers = list(range(1, 11))
return str(numbers)
if __name__ == '__main__':
app.run(port=5000)
'''
with open('app.py', 'w') as f:
f.write(CODE)
</execute_ipython>
USER:
Observation:
[Code executed successfully with no output]
ASSISTANT:
I have created a Python file `app.py` that will display a list of numbers from 1 to 10 when you run it. Let me run the Python file for you:
<execute_bash>
python3 app.py > server.log 2>&1 &
</execute_bash>
USER:
Observation:
[1] 121[1]+ Exit 1 python3 app.py > server.log 2>&1
ASSISTANT: Looks like the server is running with PID 121 then crashed. Let me check the server log:
<execute_bash>
cat server.log
</execute_bash>
USER:
Observation:
Traceback (most recent call last):
File "/workspace/app.py", line 2, in <module>
from flask import Flask
ModuleNotFoundError: No module named 'flask'
ASSISTANT:
It seems that Flask is not installed. Let me install Flask for you:
<execute_bash>
pip install flask
</execute_bash>
USER:
Observation:
Defaulting to user installation because normal site-packages is not writeable
Collecting flask
Using cached flask-3.0.3-py3-none-any.whl (101 kB)
Collecting blinker>=1.6.2
Using cached blinker-1.7.0-py3-none-any.whl (13 kB)
Collecting Werkzeug>=3.0.0
Using cached werkzeug-3.0.2-py3-none-any.whl (226 kB)
Collecting click>=8.1.3
Using cached click-8.1.7-py3-none-any.whl (97 kB)
Collecting itsdangerous>=2.1.2
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Requirement already satisfied: Jinja2>=3.1.2 in /home/opendevin/.local/lib/python3.10/site-packages (from flask) (3.1.3)
Requirement already satisfied: MarkupSafe>=2.0 in /home/opendevin/.local/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask) (2.1.5)
Installing collected packages: Werkzeug, itsdangerous, click, blinker, flask
Successfully installed Werkzeug-3.0.2 blinker-1.7.0 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0
ASSISTANT:
Now that Flask is installed, let me run the Python file again:
<execute_bash>
python3 app.py > server.log 2>&1 &
</execute_bash>
USER:
Observation:
[1] 124
ASSISTANT:
Let me check the server log again:
<execute_bash>
cat server.log
</execute_bash>
USER:
Observation:
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
ASSISTANT:
The server is running on port 5000 with PID 124. You can access the list of numbers by visiting http://127.0.0.1:5000. If you have any further questions, feel free to ask!
USER: Now kill the server, make it display the numbers in a table format.
ASSISTANT:
Sure! Let me stop the server first:
<execute_bash>
kill 124
</execute_bash>
USER:
Observation:
[1]+ Terminated python3 app.py > server.log 2>&1
ASSISTANT:
The server has been stopped. Let me open the Python file and modify it.
<execute_bash>
open app.py
</execute_bash>
USER:
[File: /workspace/app.py (11 lines total)]
1:
2:from flask import Flask
3:app = Flask(__name__)
4:
5:@app.route('/')
6:def index():
7: numbers = list(range(1, 11))
8: return str(numbers)
9:
10:if __name__ == '__main__':
11: app.run(port=5000)
ASSISTANT:
I should edit the file to display the numbers in a table format. Let me do that for you:
<execute_bash>
edit 8:8 <<EOF
return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
EOF
</execute_bash>
USER:
Observation:
[File: /workspace/app.py (11 lines total)]
1:
2:from flask import Flask
3:app = Flask(__name__)
4:
5:@app.route('/')
6:def index():
7: numbers = list(range(1, 11))
8: return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
9:
10:if __name__ == '__main__':
11: app.run(port=5000)
File updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.
ASSISTANT:
The file has been updated. Let me run the Python file again with the new changes:
<execute_bash>
python3 app.py > server.log 2>&1 &
</execute_bash>
USER:
Observation:
[1] 126
ASSISTANT:
The server is running on port 5000 with PID 126. You can access the list of numbers in a table format by visiting http://127.0.0.1:5000. Free free to let me know if you have any further requests!
--- END OF EXAMPLE ---
"""
INVALID_INPUT_MESSAGE = (
"I don't understand your input. \n"
'If you want to execute a bash command, please use <execute_bash> YOUR_COMMAND_HERE </execute_bash>.\n'
'If you want to execute a block of Python code, please use <execute_ipython> YOUR_COMMAND_HERE </execute_ipython>.\n'
)

View File

@@ -0,0 +1,5 @@
from opendevin.controller.agent import Agent
from .agent import DelegatorAgent
Agent.register('DelegatorAgent', DelegatorAgent)

View File

@@ -0,0 +1,85 @@
from typing import List
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
class DelegatorAgent(Agent):
"""
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
"""
current_delegate: str = ''
def __init__(self, llm: LLM):
"""
Initialize the Delegator Agent with an LLM
Parameters:
- llm (LLM): The llm to be used by this agent
"""
super().__init__(llm)
def step(self, state: State) -> Action:
"""
Checks to see if current step is completed, returns AgentFinishAction if True.
Otherwise, creates a plan prompt and sends to model for inference, returning the result as the next action.
Parameters:
- state (State): The current state given the previous actions and observations
Returns:
- AgentFinishAction: If the last state was 'completed', 'verified', or 'abandoned'
- Action: The next action to take based on llm response
"""
if self.current_delegate == '':
self.current_delegate = 'study'
return AgentDelegateAction(
agent='StudyRepoForTaskAgent', inputs={'task': state.plan.main_goal}
)
lastObservation = state.history[-1][1]
if not isinstance(lastObservation, AgentDelegateObservation):
raise Exception('Last observation is not an AgentDelegateObservation')
if self.current_delegate == 'study':
self.current_delegate = 'coder'
return AgentDelegateAction(
agent='Coder',
inputs={
'task': state.plan.main_goal,
'summary': lastObservation.outputs['summary'],
},
)
elif self.current_delegate == 'coder':
self.current_delegate = 'verifier'
return AgentDelegateAction(
agent='Verifier',
inputs={
'task': state.plan.main_goal,
},
)
elif self.current_delegate == 'verifier':
if (
'completed' in lastObservation.outputs
and lastObservation.outputs['completed']
):
return AgentFinishAction()
else:
self.current_delegate = 'coder'
return AgentDelegateAction(
agent='Coder',
inputs={
'task': state.plan.main_goal,
'summary': lastObservation.outputs['summary'],
},
)
else:
raise Exception('Invalid delegate state')
def search_memory(self, query: str) -> List[str]:
return []

View File

@@ -0,0 +1,5 @@
from opendevin.controller.agent import Agent
from .agent import DummyAgent
Agent.register('DummyAgent', DummyAgent)

View File

@@ -0,0 +1,159 @@
import time
from typing import List, TypedDict
from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Action,
AddTaskAction,
AgentFinishAction,
AgentRecallAction,
AgentThinkAction,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
ModifyTaskAction,
)
from opendevin.events.observation import (
AgentRecallObservation,
CmdOutputObservation,
FileReadObservation,
FileWriteObservation,
NullObservation,
Observation,
)
from opendevin.llm.llm import LLM
"""
FIXME: There are a few problems this surfaced
* FileWrites seem to add an unintended newline at the end of the file
* command_id is sometimes a number, sometimes a string
* Why isn't the output of the background command split between two steps?
* Browser not working
"""
ActionObs = TypedDict(
'ActionObs', {'action': Action, 'observations': List[Observation]}
)
BACKGROUND_CMD = 'echo "This is in the background" && sleep .1 && echo "This too"'
class DummyAgent(Agent):
"""
The DummyAgent is used for e2e testing. It just sends the same set of actions deterministically,
without making any LLM calls.
"""
def __init__(self, llm: LLM):
super().__init__(llm)
self.steps: List[ActionObs] = [
{
'action': AddTaskAction(parent='0', goal='check the current directory'),
'observations': [NullObservation('')],
},
{
'action': AddTaskAction(parent='0.0', goal='run ls'),
'observations': [NullObservation('')],
},
{
'action': ModifyTaskAction(id='0.0', state='in_progress'),
'observations': [NullObservation('')],
},
{
'action': AgentThinkAction(thought='Time to get started!'),
'observations': [NullObservation('')],
},
{
'action': CmdRunAction(command='echo "foo"'),
'observations': [
CmdOutputObservation('foo', command_id=-1, command='echo "foo"')
],
},
{
'action': FileWriteAction(
content='echo "Hello, World!"', path='hello.sh'
),
'observations': [FileWriteObservation('', path='hello.sh')],
},
{
'action': FileReadAction(path='hello.sh'),
'observations': [
FileReadObservation('echo "Hello, World!"\n', path='hello.sh')
],
},
{
'action': CmdRunAction(command='bash hello.sh'),
'observations': [
CmdOutputObservation(
'Hello, World!', command_id=-1, command='bash hello.sh'
)
],
},
{
'action': CmdRunAction(command=BACKGROUND_CMD, background=True),
'observations': [
CmdOutputObservation(
'Background command started. To stop it, send a `kill` action with id 42',
command_id='42', # 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)
if state.iteration > 0:
prev_step = self.steps[state.iteration - 1]
if 'observations' in prev_step:
expected_observations = prev_step['observations']
hist_start = len(state.history) - len(expected_observations)
for i in range(len(expected_observations)):
hist_obs = state.history[hist_start + i][1].to_dict()
expected_obs = expected_observations[i].to_dict()
if (
'command_id' in hist_obs['extras']
and hist_obs['extras']['command_id'] != -1
):
del hist_obs['extras']['command_id']
hist_obs['content'] = ''
if (
'command_id' in expected_obs['extras']
and expected_obs['extras']['command_id'] != -1
):
del expected_obs['extras']['command_id']
expected_obs['content'] = ''
if hist_obs != expected_obs:
print('\nactual', hist_obs)
print('\nexpect', expected_obs)
assert (
hist_obs == expected_obs
), f'Expected observation {expected_obs}, got {hist_obs}'
return self.steps[state.iteration]['action']
def search_memory(self, query: str) -> List[str]:
return ['I am a computer.']

14
agenthub/micro/README.md Normal file
View File

@@ -0,0 +1,14 @@
## Introduction
This package contains definitions of micro-agents. A micro-agent is defined
in the following structure:
```
[AgentName]
├── agent.yaml
└── prompt.md
```
Note that `prompt.md` could use jinja2 template syntax. During runtime, `prompt.md`
is loaded and rendered, and used together with `agent.yaml` to initialize a
micro-agent.

View File

@@ -0,0 +1,2 @@
* `browse` - opens a web page. Arguments:
* `url` - the URL to open

View File

@@ -0,0 +1,3 @@
* `delegate` - send a task to another agent from the list provided. Arguments:
* `agent` - the agent to which the task is delegated. MUST match a name in the list of agents provided.
* `inputs` - a dictionary of input parameters to the agent, as specified in the list

View File

@@ -0,0 +1,2 @@
* `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

View File

@@ -0,0 +1,2 @@
* `kill` - kills a background command
* `id` - the ID of the background command to kill

View File

@@ -0,0 +1,2 @@
* `read` - reads the content of a file. Arguments:
* `path` - the path of the file to read

View File

@@ -0,0 +1,3 @@
* `run` - runs a command on the command line in a Linux shell. Arguments:
* `command` - the command to run
* `background` - if true, run the command in the background, so that other commands can be run concurrently. Useful for e.g. starting a server. You won't be able to see the logs. You don't need to end the command with `&`, just set this to true.

View File

@@ -0,0 +1,2 @@
* `think` - make a plan, set a goal, or record your thoughts. Arguments:
* `thought` - the thought to record

View File

@@ -0,0 +1,3 @@
* `write` - writes the content to a file. Arguments:
* `path` - the path of the file to write
* `content` - the content to write to the file

View File

@@ -0,0 +1,5 @@
Your response MUST be in JSON format. It must be an object, and it must contain two fields:
* `action`, which is one of the actions specified here
* `args`, which is a map of key-value pairs, specifying the arguments for that action
You MUST NOT include any other text besides the JSON response

View File

@@ -0,0 +1,4 @@
Here is a recent history of actions you've taken in service of this plan,
as well as observations you've made. This only includes the MOST RECENT
actions and observations--more may have happened before that.
They are time-ordered, with your most recent action at the bottom.

107
agenthub/micro/agent.py Normal file
View File

@@ -0,0 +1,107 @@
import copy
import json
from typing import Dict, List
from jinja2 import BaseLoader, Environment
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 .instructions import instructions
from .registry import all_microagents
def parse_response(orig_response: str) -> Action:
json_start = orig_response.find('{')
json_end = orig_response.rfind('}') + 1
response = orig_response[json_start:json_end]
try:
action_dict = json.loads(response)
except json.JSONDecodeError as e:
raise LLMOutputError(
'Invalid JSON in response. Please make sure the response is a valid JSON object'
) from e
action = action_from_dict(action_dict)
return action
def my_encoder(obj):
"""
Encodes objects as dictionaries
Parameters:
- obj (Object): An object that will be converted
Returns:
- dict: If the object can be converted it is returned in dict format
"""
if hasattr(obj, 'to_dict'):
return obj.to_dict()
def _remove_fields(obj, fields: set[str]):
"""
Remove fields from an object
Parameters:
- obj (Object): The object to remove fields from
- fields (set[str]): A set of field names to remove from the object
"""
if isinstance(obj, dict):
for field in fields:
if field in obj:
del obj[field]
for _, value in obj.items():
_remove_fields(value, fields)
elif isinstance(obj, list) or isinstance(obj, tuple):
for item in obj:
_remove_fields(item, fields)
elif hasattr(obj, '__dataclass_fields__'):
for field in fields:
if field in obj.__dataclass_fields__:
setattr(obj, field, None)
for value in obj.__dict__.values():
_remove_fields(value, fields)
def to_json(obj, **kwargs):
"""
Serialize an object to str format
"""
# Remove things like screenshots that shouldn't be in a prompt
sanitized_obj = copy.deepcopy(obj)
_remove_fields(sanitized_obj, {'screenshot'})
return json.dumps(sanitized_obj, default=my_encoder, **kwargs)
class MicroAgent(Agent):
prompt = ''
agent_definition: Dict = {}
def __init__(self, llm: LLM):
super().__init__(llm)
if 'name' not in self.agent_definition:
raise ValueError('Agent definition must contain a name')
self.prompt_template = Environment(loader=BaseLoader).from_string(self.prompt)
self.delegates = all_microagents.copy()
del self.delegates[self.agent_definition['name']]
def step(self, state: State) -> Action:
prompt = self.prompt_template.render(
state=state,
instructions=instructions,
to_json=to_json,
delegates=self.delegates,
)
messages = [{'content': prompt, 'role': 'user'}]
resp = self.llm.completion(messages=messages)
action_resp = resp['choices'][0]['message']['content']
state.num_of_chars += len(prompt) + len(action_resp)
action = parse_response(action_resp)
return action
def search_memory(self, query: str) -> List[str]:
return []

View File

@@ -0,0 +1,6 @@
name: CoderAgent
description: Given a particular task, and a detailed description of the codebase, accomplishes the task
inputs:
task: string
codebase_summary: string
outputs: {}

View File

@@ -0,0 +1,27 @@
# Task
You are a software engineer. You've inherited an existing codebase, which you
need to modify to complete this task:
{{ state.plan.main_goal }}
{% if state.inputs.summary %}
Here's a summary of the codebase, as it relates to this task:
{{ state.inputs.summary }}
{% endif %}
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.write }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
Do NOT finish until you have completed the tasks.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}

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.

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

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 }}

View File

@@ -0,0 +1,20 @@
import os
from typing import Dict
instructions: Dict = {}
base_dir = os.path.dirname(os.path.abspath(__file__)) + '/_instructions'
for root, dirs, files in os.walk(base_dir):
if len(files) == 0:
continue
rel_base = os.path.relpath(root, base_dir)
keys = rel_base.split('/')
obj = instructions
for key in keys:
if key not in obj:
obj[key] = {}
obj = obj[key]
for file in files:
without_ext = os.path.splitext(file)[0]
with open(os.path.join(root, file), 'r') as f:
obj[without_ext] = f.read()

View File

@@ -0,0 +1,6 @@
name: ManagerAgent
description: Delegates tasks to microagents based on their area of expertise
generates: Action
inputs:
task: string
outputs: {}

View File

@@ -0,0 +1,27 @@
# Task
You are in charge of accomplishing the following task:
{{ state.plan.main_goal }}
In order to accomplish this goal, you must delegate tasks to one or more agents, who
can do the actual work. A description of each agent is provided below. You MUST
select one of the delegates below to move towards accomplishing the task, and you MUST
provide the correct inputs for the delegate you select.
## Agents
{% for name, details in delegates.items() %}
### {{ name }}
{{ details.description }}
#### Inputs
{{ to_json(details.inputs) }}
{% endfor %}
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Available Actions
{{ instructions.actions.delegate }}
{{ instructions.actions.finish }}
## Format
{{ instructions.format.action }}

View File

@@ -0,0 +1,25 @@
name: MathAgent
description: "Solves simple and complex math problems using python"
generates: Action
container: python:3.12.3-bookworm
inputs:
task: string
outputs:
answer: string
examples:
- inputs:
task: "What is 2 + 2?"
outputs:
answer: "4"
- inputs:
task: "What is the area of a circle with radius 7.324 inches?"
output:
answer: "168.518 square inches"
- inputs:
task: "What day of the week is 2099-01-01?"
outputs:
answer: "Saturday"
- inputs:
task: "What is the integral of sin(x^2) evaluated from -1 to 1?"
outputs:
answer: "0.603848"

View File

@@ -0,0 +1,23 @@
# Task
You are a brilliant mathematician and programmer. You've been given the following problem to solve:
{{ state.plan.main_goal }}
Please write a python script that solves this problem, and prints the answer to stdout.
ONLY print the answer to stdout, nothing else.
You should then run the python script with `python3`,
and call the `finish` action with `outputs.answer` set to the answer.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
If the last item in the history is an error, you should try to fix it.
## Available Actions
{{ instructions.actions.write }}
{{ instructions.actions.run }}
{{ instructions.actions.finish }}
## Format
{{ instructions.format.action }}

View File

@@ -0,0 +1,6 @@
name: PostgresAgent
description: Writes and maintains PostgreSQL migrations
generates: Action
inputs:
task: string
outputs: {}

View File

@@ -0,0 +1,24 @@
# Task
You are a database engineer. You are working on an existing Postgres project, and have been given
the following task:
{{ state.plan.main_goal }}
You must:
* Investigate the existing migrations to understand the current schema
* Write a new migration to accomplish the task above
* Test that the migrations work properly
## Actions
You may take any of the following actions:
{{ instructions.actions.think }}
{{ instructions.actions.read }}
{{ instructions.actions.write }}
{{ instructions.actions.run }}
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}

View File

@@ -0,0 +1,25 @@
import os
import yaml
all_microagents = {}
for dir in os.listdir(os.path.dirname(__file__)):
base = os.path.dirname(__file__) + '/' + dir
if os.path.isfile(base):
continue
if dir.startswith('_'):
continue
promptFile = base + '/prompt.md'
agentFile = base + '/agent.yaml'
if not os.path.isfile(promptFile) or not os.path.isfile(agentFile):
raise Exception(
f'Missing prompt or agent file in {base}. Please create them.')
with open(promptFile, 'r') as f:
prompt = f.read()
with open(agentFile, 'r') as f:
agent = yaml.safe_load(f)
if 'name' not in agent:
raise Exception(f'Missing name in {agentFile}')
agent['prompt'] = prompt
all_microagents[agent['name']] = agent

View File

@@ -0,0 +1,5 @@
name: RepoExplorerAgent
description: Generates a detailed summary of an existing codebase
inputs: {}
outputs:
summary: string

View File

@@ -0,0 +1,26 @@
# Task
You are a software engineer. You've inherited an existing codebase, which you're
learning about for the first time. Your goal is to produce a detailed summary
of the codebase, including:
* The overall purpose of the project
* The directory structure
* The main components of the codebase
* How the components fit together
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
You should ONLY `run` commands that have no side-effects, like `ls` and `grep`.
Do NOT finish until you have a complete understanding of the codebase.
When you're done, put your summary into the output of the `finish` action.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}

View File

@@ -0,0 +1,6 @@
name: StudyRepoForTaskAgent
description: Given a particular task, finds and describes all relevant parts of the codebase
inputs:
task: string
outputs:
summary: string

View File

@@ -0,0 +1,25 @@
# Task
You are a software engineer. You've inherited an existing codebase, which you're
learning about for the first time. You need to study the codebase to find all
the information needed to complete this task:
{{ state.plan.main_goal }}
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
You must ONLY `run` commands that have no side-effects, like `ls` and `grep`.
Do NOT finish until you have a complete understanding of which parts of the
codebase are relevant to the task, including particular files, functions, and classes.
When you're done, put your summary in `outputs.summary` in the `finish` action.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}

View File

@@ -0,0 +1,7 @@
name: VerifierAgent
description: Given a particular task, verifies that the task has been completed
inputs:
task: string
outputs:
completed: boolean
summary: string

View File

@@ -0,0 +1,27 @@
# Task
You are a quality assurance engineer. Another engineer has made changes to the
codebase which are supposed to solve this task:
{{ state.plan.main_goal }}
Your goal is to verify that the changes are correct and bug-free.
## Available Actions
{{ instructions.actions.run }}
{{ instructions.actions.read }}
{{ instructions.actions.think }}
{{ instructions.actions.finish }}
You must ONLY `run` commands that have no side-effects, like `ls`, `grep`, and test scripts.
Do NOT finish until you know whether the task is complete and correct.
When you're done, add a `completed` boolean to the `outputs` of the `finish` action.
If `completed` is `false`, you MUST also provide a `summary` in the `outputs` of the `finish` action
explaining what the problem is.
## History
{{ instructions.history_truncated }}
{{ to_json(state.history[-10:]) }}
## Format
{{ instructions.format.action }}

View File

@@ -1,4 +1,5 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import MonologueAgent
Agent.register('MonologueAgent', MonologueAgent)

View File

@@ -1,33 +1,36 @@
from typing import List
from opendevin.agent import Agent
from opendevin.state import State
from opendevin.llm.llm import LLM
from opendevin.schema import ActionType, ObservationType
from opendevin.exceptions import AgentNoInstructionError
from opendevin.action import (
Action,
NullAction,
CmdRunAction,
FileWriteAction,
FileReadAction,
AgentRecallAction,
BrowseURLAction,
AgentThinkAction,
)
from opendevin.observation import (
Observation,
NullObservation,
CmdOutputObservation,
FileReadObservation,
AgentRecallObservation,
BrowserOutputObservation,
)
import agenthub.monologue_agent.utils.prompts as prompts
from agenthub.monologue_agent.utils.monologue import Monologue
from agenthub.monologue_agent.utils.memory import LongTermMemory
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,
BrowseURLAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
GitHubPushAction,
NullAction,
)
from opendevin.events.observation import (
AgentRecallObservation,
BrowserOutputObservation,
CmdOutputObservation,
FileReadObservation,
NullObservation,
Observation,
)
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_OUTPUT_LENGTH = 5000
@@ -53,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',
@@ -68,13 +71,17 @@ INITIAL_THOUGHTS = [
'BROWSE google.com',
'<form><input type="text"></input><button type="submit"></button></form>',
'I can browse the web too!',
'If I have done some work and I want to push it to github, I can do that also!',
"Let's do it.",
'PUSH owner/repo branch',
'The repo was successfully pushed to https://github.com/owner/repo/branch',
'And once I have completed my task, I can use the finish action to stop working.',
"But I should only use the finish action when I'm absolutely certain that I've completed my task and have tested my work.",
'Very cool. Now to accomplish my task.',
"I'll need a strategy. And as I make progress, I'll need to keep refining that strategy. I'll need to set goals, and break them into sub-goals.",
'In between actions, I must always take some time to think, strategize, and set new goals. I should never take two actions in a row.',
"OK so my task is to $TASK. I haven't made any progress yet. Where should I start?",
"It seems like there might be an existing project here. I should probably start by running `ls` to see what's here.",
'It seems like there might be an existing project here. I should probably start by running `pwd` and `ls` to orient myself.',
]
@@ -86,6 +93,8 @@ class MonologueAgent(Agent):
"""
_initialized = False
monologue: Monologue
memory: 'LongTermMemory | None'
def __init__(self, llm: LLM):
"""
@@ -95,8 +104,6 @@ class MonologueAgent(Agent):
- llm (LLM): The llm to be used by this agent
"""
super().__init__(llm)
self.monologue = Monologue()
self.memory = LongTermMemory()
def _add_event(self, event: dict):
"""
@@ -119,14 +126,17 @@ class MonologueAgent(Agent):
)
self.monologue.add_event(event)
self.memory.add_event(event)
if self.memory is not None:
self.memory.add_event(event)
if self.monologue.get_total_length() > MAX_MONOLOGUE_LENGTH:
self.monologue.condense(self.llm)
def _initialize(self, task: str):
"""
Utilizes the INITIAL_THOUGHTS list to give the agent a context for it's capabilities and how to navigate the /workspace.
Short circuted to return when already initialized.
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.
Parameters:
- task (str): The initial goal statement provided by the user
@@ -140,35 +150,42 @@ class MonologueAgent(Agent):
if task is None or task == '':
raise AgentNoInstructionError()
self.monologue = Monologue()
self.memory = LongTermMemory()
output_type = ''
self.monologue = Monologue()
if config.get(ConfigType.AGENT_MEMORY_ENABLED):
self.memory = LongTermMemory()
else:
self.memory = None
self._add_initial_thoughts(task)
self._initialized = True
def _add_initial_thoughts(self, task):
previous_action = ''
for thought in INITIAL_THOUGHTS:
thought = thought.replace('$TASK', task)
if output_type != '':
if previous_action != '':
observation: Observation = NullObservation(content='')
if output_type == ObservationType.RUN:
if previous_action in {ActionType.RUN, ActionType.PUSH}:
observation = CmdOutputObservation(
content=thought, command_id=0, command=''
)
elif output_type == ObservationType.READ:
elif previous_action == ActionType.READ:
observation = FileReadObservation(content=thought, path='')
elif output_type == ObservationType.RECALL:
observation = AgentRecallObservation(
content=thought, memories=[])
elif output_type == ObservationType.BROWSE:
elif previous_action == ActionType.RECALL:
observation = AgentRecallObservation(content=thought, memories=[])
elif previous_action == ActionType.BROWSE:
observation = BrowserOutputObservation(
content=thought, url='', screenshot=''
)
self._add_event(observation.to_dict())
output_type = ''
self._add_event(observation.to_memory())
previous_action = ''
else:
action: Action = NullAction()
if thought.startswith('RUN'):
command = thought.split('RUN ')[1]
action = CmdRunAction(command)
output_type = ActionType.RUN
previous_action = ActionType.RUN
elif thought.startswith('WRITE'):
parts = thought.split('WRITE ')[1].split(' > ')
path = parts[1]
@@ -177,19 +194,23 @@ class MonologueAgent(Agent):
elif thought.startswith('READ'):
path = thought.split('READ ')[1]
action = FileReadAction(path=path)
output_type = ActionType.READ
previous_action = ActionType.READ
elif thought.startswith('RECALL'):
query = thought.split('RECALL ')[1]
action = AgentRecallAction(query=query)
output_type = ActionType.RECALL
previous_action = ActionType.RECALL
elif thought.startswith('BROWSE'):
url = thought.split('BROWSE ')[1]
action = BrowseURLAction(url=url)
output_type = ActionType.BROWSE
previous_action = ActionType.BROWSE
elif thought.startswith('PUSH'):
owner_repo, branch = thought.split('PUSH ')[1].split(' ')
owner, repo = owner_repo.split('/')
action = GitHubPushAction(owner=owner, repo=repo, branch=branch)
previous_action = ActionType.PUSH
else:
action = AgentThinkAction(thought=thought)
self._add_event(action.to_dict())
self._initialized = True
self._add_event(action.to_memory())
def step(self, state: State) -> Action:
"""
@@ -203,8 +224,8 @@ class MonologueAgent(Agent):
"""
self._initialize(state.plan.main_goal)
for prev_action, obs in state.updated_info:
self._add_event(prev_action.to_dict())
self._add_event(obs.to_dict())
self._add_event(prev_action.to_memory())
self._add_event(obs.to_memory())
state.updated_info = []
@@ -232,4 +253,12 @@ class MonologueAgent(Agent):
Returns:
- List[str]: A list of top 10 text results that matched the query
"""
if self.memory is None:
return []
return self.memory.search(query)
def reset(self) -> None:
super().reset()
# Reset the initial monologue and memory
self._initialized = False

View File

@@ -1,4 +1,5 @@
import json
from json_repair import repair_json

View File

@@ -1,44 +1,115 @@
import chromadb
from llama_index.core import Document
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core import VectorStoreIndex
from llama_index.vector_stores.chroma import ChromaVectorStore
import threading
import chromadb
import llama_index.embeddings.openai.base as llama_openai
from llama_index.core import Document, VectorStoreIndex
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.vector_stores.chroma import ChromaVectorStore
from openai._exceptions import APIConnectionError, InternalServerError, RateLimitError
from tenacity import (
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from opendevin.core import config
from opendevin.core.logger import opendevin_logger as logger
from opendevin.core.schema.config import ConfigType
from opendevin import config
from . import json
embedding_strategy = config.get('LLM_EMBEDDING_MODEL')
num_retries = config.get(ConfigType.LLM_NUM_RETRIES)
retry_min_wait = config.get(ConfigType.LLM_RETRY_MIN_WAIT)
retry_max_wait = config.get(ConfigType.LLM_RETRY_MAX_WAIT)
# llama-index includes a retry decorator around openai.get_embeddings() function
# it is initialized with hard-coded values and errors
# this non-customizable behavior is creating issues when it's retrying faster than providers' rate limits
# this block attempts to banish it and replace it with our decorator, to allow users to set their own limits
if hasattr(llama_openai.get_embeddings, '__wrapped__'):
original_get_embeddings = llama_openai.get_embeddings.__wrapped__
else:
logger.warning('Cannot set custom retry limits.') # warn
num_retries = 1
original_get_embeddings = llama_openai.get_embeddings
def attempt_on_error(retry_state):
logger.error(
f'{retry_state.outcome.exception()}. Attempt #{retry_state.attempt_number} | You can customize these settings in the configuration.',
exc_info=False,
)
return True
@retry(
reraise=True,
stop=stop_after_attempt(num_retries),
wait=wait_random_exponential(min=retry_min_wait, max=retry_max_wait),
retry=retry_if_exception_type(
(RateLimitError, APIConnectionError, InternalServerError)
),
after=attempt_on_error,
)
def wrapper_get_embeddings(*args, **kwargs):
return original_get_embeddings(*args, **kwargs)
llama_openai.get_embeddings = wrapper_get_embeddings
embedding_strategy = config.get(ConfigType.LLM_EMBEDDING_MODEL)
# TODO: More embeddings: https://docs.llamaindex.ai/en/stable/examples/embeddings/OpenAI/
# There's probably a more programmatic way to do this.
if embedding_strategy == 'llama2':
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='llama2',
base_url=config.get('LLM_BASE_URL', required=True),
model_name=embedding_strategy,
base_url=config.get(ConfigType.LLM_EMBEDDING_BASE_URL, required=True),
ollama_additional_kwargs={'mirostat': 0},
)
elif embedding_strategy == 'openai':
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding(
model='text-embedding-ada-002',
api_key=config.get('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('LLM_DEPLOYMENT_NAME', required=True),
api_key=config.get('LLM_API_KEY', required=True),
azure_endpoint=config.get('LLM_BASE_URL', required=True),
api_version=config.get('LLM_API_VERSION', required=True),
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),
)
elif (embedding_strategy is not None) and (embedding_strategy.lower() == 'none'):
# TODO: this works but is not elegant enough. The incentive is when
# monologue agent is not used, there is no reason we need to initialize an
# embedding model
embed_model = None
else:
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
embed_model = HuggingFaceEmbedding(
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))
class LongTermMemory:
@@ -55,8 +126,10 @@ 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 = []
def add_event(self, event: dict):
"""
@@ -83,7 +156,14 @@ class LongTermMemory:
},
)
self.thought_idx += 1
self.index.insert(doc)
logger.debug('Adding %s event to memory: %d', t, self.thought_idx)
thread = threading.Thread(target=self._add_doc, args=(doc,))
self._add_threads.append(thread)
thread.start() # We add the doc concurrently so we don't have to wait ~500ms for the insert
def _add_doc(self, doc):
with sema:
self.index.insert(doc)
def search(self, query: str, k: int = 10):
"""

View File

@@ -1,9 +1,8 @@
import traceback
from opendevin.llm.llm import LLM
from opendevin.exceptions import AgentEventTypeError
import agenthub.monologue_agent.utils.json as json
import agenthub.monologue_agent.utils.prompts as prompts
from opendevin.core.exceptions import AgentEventTypeError
from opendevin.core.logger import opendevin_logger as logger
from opendevin.llm.llm import LLM
class Monologue:
@@ -53,7 +52,7 @@ class Monologue:
try:
total_length += len(json.dumps(t))
except TypeError as e:
print(f'Error serializing thought: {e}')
logger.error('Error serializing thought: %s', str(e), exc_info=False)
return total_length
def condense(self, llm: LLM):
@@ -64,7 +63,7 @@ class Monologue:
- llm (LLM): llm to be used for summarization
Raises:
- RunTimeError: When the condensing process fails for any reason
- Exception: the same exception as it got from the llm or processing the response
"""
try:
@@ -74,5 +73,7 @@ class Monologue:
summary_resp = resp['choices'][0]['message']['content']
self.thoughts = prompts.parse_summary_response(summary_resp)
except Exception as e:
traceback.print_exc()
raise RuntimeError(f'Error condensing thoughts: {e}')
logger.error('Error condensing thoughts: %s', str(e), exc_info=False)
# TODO If the llm fails with ContextWindowExceededError, we can try to condense the monologue chunk by chunk
raise

View File

@@ -1,18 +1,19 @@
import re
from json import JSONDecodeError
from typing import List
from . import json
from json import JSONDecodeError
import re
from opendevin.action import (
action_from_dict,
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.observation import (
from opendevin.events.observation import (
CmdOutputObservation,
)
from opendevin.exceptions import LLMOutputError
from . import json
ACTION_PROMPT = """
You're a thoughtful robot. Your main task is this:
@@ -26,8 +27,8 @@ This is your internal monologue, in JSON format:
Your most recent thought is at the bottom of that monologue. Continue your train of thought.
What is your next thought or action? Your response must be in JSON format.
It must be an object, and it must contain two fields:
What is your next single thought or action? Your response must be in JSON format.
It must be a single object, and it must contain two fields:
* `action`, which is one of the actions below
* `args`, which is a map of key-value pairs, specifying the arguments for that action
@@ -44,6 +45,10 @@ Here are the possible actions:
* `id` - the ID of the background command to kill
* `browse` - opens a web page. Arguments:
* `url` - the URL to open
* `push` - Push a branch from the current repo to github:
* `owner` - the owner of the repo to push to
* `repo` - the name of the repo to push to
* `branch` - the name of the branch to push
* `recall` - recalls a past memory. Arguments:
* `query` - the query to search for
* `think` - make a plan, set a goal, or record your thoughts. Arguments:
@@ -52,16 +57,20 @@ Here are the possible actions:
%(background_commands)s
You MUST take time to think in between read, write, run, browse, and recall actions.
You MUST take time to think in between read, write, run, browse, push, and recall actions.
You should never act twice in a row without thinking. But if your last several
actions are all "think" actions, you should consider taking a different action.
Notes:
* your environment is Debian Linux. You can install software with `apt`
* your working directory will not change, even if you run `cd`. All commands will be run in the `/workspace` directory.
* you are logged in as %(user)s, but sudo will always work without a password.
* all non-background commands will be forcibly stopped if they remain running for over %(timeout)s seconds.
* your environment is Debian Linux. You can install software with `sudo apt-get`, but remember to use -y.
* don't run interactive commands, or commands that don't return (e.g. `node server.js`). You may run commands in the background (e.g. `node server.js &`)
* don't run interactive text editors (e.g. `nano` or 'vim'), instead use the 'write' or 'read' action.
* don't run gui applications (e.g. software IDEs (like vs code or codium), web browsers (like firefox or chromium), or other complex software packages). Use non-interactive cli applications, or special actions instead.
* whenever an action fails, always `think` about why it may have happened before acting again.
What is your next thought or action? Again, you must reply with JSON, and only with JSON.
What is your next single thought or action? Again, you must reply with JSON, and only with JSON. You must respond with exactly one 'action' object.
%(hint)s
"""
@@ -140,11 +149,18 @@ def get_request_action_prompt(
)
bg_commands_message += '\nYou can end any process by sending a `kill` action with the numerical `id` above.'
user = 'opendevin' if config.get(ConfigType.RUN_AS_DEVIN) else 'root'
return ACTION_PROMPT % {
'task': task,
'monologue': json.dumps(thoughts, indent=2),
'background_commands': bg_commands_message,
'hint': hint,
'user': user,
'timeout': config.get(ConfigType.SANDBOX_TIMEOUT),
'WORKSPACE_MOUNT_PATH_IN_SANDBOX': config.get(
ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX
),
}
@@ -164,16 +180,26 @@ 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
except ValueError as e:
action_dict = json.loads(
max(response_json_matches, key=rank)[0]
) # Use the highest ranked response
except (ValueError, JSONDecodeError):
raise LLMOutputError(
"Output from the LLM isn't properly formatted. The model may be misconfigured."
) from e
'Invalid JSON, the response must be well-formed JSON as specified in the prompt.'
)
except (ValueError, TypeError):
raise LLMOutputError(
'Invalid JSON, the response must be well-formed JSON as specified in the prompt.'
)
if 'content' in action_dict:
# The LLM gets confused here. Might as well be robust
action_dict['contents'] = action_dict.pop('content')

View File

@@ -1,4 +1,5 @@
from opendevin.agent import Agent
from opendevin.controller.agent import Agent
from .agent import PlannerAgent
Agent.register('PlannerAgent', PlannerAgent)

View File

@@ -1,11 +1,11 @@
from typing import List
from .prompt import get_prompt, parse_response
from opendevin.agent import Agent
from opendevin.action import AgentFinishAction
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 opendevin.action import Action
from .prompt import get_prompt, parse_response
class PlannerAgent(Agent):

View File

@@ -1,29 +1,28 @@
import json
from typing import List, Tuple, Dict, Type
from typing import Dict, List, Tuple, Type
from opendevin.controller.agent_controller import print_with_color
from opendevin.plan import Plan
from opendevin.action import Action, action_from_dict
from opendevin.observation import Observation
from opendevin.schema import ActionType
from opendevin.action import (
NullAction,
CmdRunAction,
CmdKillAction,
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,
AgentRecallAction,
AgentSummarizeAction,
AgentThinkAction,
BrowseURLAction,
CmdKillAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
AgentRecallAction,
AgentThinkAction,
AgentFinishAction,
AgentSummarizeAction,
AddTaskAction,
ModifyTaskAction,
NullAction,
action_from_dict,
)
from opendevin.observation import (
from opendevin.events.observation import (
NullObservation,
Observation,
)
ACTION_TYPE_TO_CLASS: Dict[str, Type[Action]] = {
@@ -131,6 +130,25 @@ What is your next thought or action? Again, you must reply with JSON, and only w
"""
def get_hint(latest_action_id: str) -> str:
"""Returns action type hint based on given action_id"""
hints = {
'': "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with.",
ActionType.RUN: 'You should think about the command you just ran, what output it gave, and how that affects your plan.',
ActionType.READ: 'You should think about the file you just read, what you learned from it, and how that affects your plan.',
ActionType.WRITE: 'You just changed a file. You should think about how it affects your plan.',
ActionType.BROWSE: 'You should think about the page you just visited, and what you learned from it.',
ActionType.THINK: "Look at your last thought in the history above. What does it suggest? Don't think anymore--take action.",
ActionType.RECALL: 'You should think about the information you just recalled, and how it should affect your plan.',
ActionType.ADD_TASK: 'You should think about the next action to take.',
ActionType.MODIFY_TASK: 'You should think about the next action to take.',
ActionType.SUMMARIZE: '',
ActionType.FINISH: '',
}
return hints.get(latest_action_id, '')
def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
"""
Gets the prompt for the planner agent.
@@ -150,10 +168,10 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
latest_action: Action = NullAction()
for action, observation in sub_history:
if not isinstance(action, NullAction):
history_dicts.append(action.to_dict())
history_dicts.append(action.to_memory())
latest_action = action
if not isinstance(observation, NullObservation):
observation_dict = observation.to_dict()
observation_dict = observation.to_memory()
if (
'extras' in observation_dict
and 'screenshot' in observation_dict['extras']
@@ -161,8 +179,6 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
del observation_dict['extras']['screenshot']
history_dicts.append(observation_dict)
history_str = json.dumps(history_dicts, indent=2)
hint = ''
current_task = plan.get_current_task()
if current_task is not None:
plan_status = f"You're currently working on this task:\n{current_task.goal}."
@@ -170,35 +186,8 @@ def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
plan_status += "\nIf it's not achievable AND verifiable with a SINGLE action, you MUST break it down into subtasks NOW."
else:
plan_status = "You're not currently working on any tasks. Your next action MUST be to mark a task as in_progress."
hint = plan_status
latest_action_id = latest_action.to_dict()['action']
if current_task is not None:
if latest_action_id == '':
hint = "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with."
elif latest_action_id == ActionType.RUN:
hint = 'You should think about the command you just ran, what output it gave, and how that affects your plan.'
elif latest_action_id == ActionType.READ:
hint = 'You should think about the file you just read, what you learned from it, and how that affects your plan.'
elif latest_action_id == ActionType.WRITE:
hint = 'You just changed a file. You should think about how it affects your plan.'
elif latest_action_id == ActionType.BROWSE:
hint = 'You should think about the page you just visited, and what you learned from it.'
elif latest_action_id == ActionType.THINK:
hint = "Look at your last thought in the history above. What does it suggest? Don't think anymore--take action."
elif latest_action_id == ActionType.RECALL:
hint = 'You should think about the information you just recalled, and how it should affect your plan.'
elif latest_action_id == ActionType.ADD_TASK:
hint = 'You should think about the next action to take.'
elif latest_action_id == ActionType.MODIFY_TASK:
hint = 'You should think about the next action to take.'
elif latest_action_id == ActionType.SUMMARIZE:
hint = ''
elif latest_action_id == ActionType.FINISH:
hint = ''
print_with_color('HINT:\n' + hint, 'INFO')
hint = get_hint(latest_action.to_dict()['action'])
logger.info('HINT:\n' + hint, extra={'msg_type': 'INFO'})
return prompt % {
'task': plan.main_goal,
'plan': plan_str,

13
containers/README.md Normal file
View File

@@ -0,0 +1,13 @@
# Docker Containers
Each folder here contains a Dockerfile, and a config.sh describing how to build
the image and where to push it. These are images are built and pushed in GitHub Actions
by the `ghcr.yml` workflow.
## Building Manually
```
docker build -f containers/app/Dockerfile -t opendevin .
docker build -f containers/sandbox/Dockerfile -t sandbox .
docker build -f containers/evaluation/Dockerfile -t evaluation evaluation/SWE-bench/
```

View File

@@ -1,9 +1,11 @@
ARG OPEN_DEVIN_BUILD_VERSION=dev
FROM node:21.7.2-bookworm-slim as frontend-builder
WORKDIR /app
COPY ./frontend/package.json frontend/package-lock.json ./
RUN npm install
RUN npm install -g npm@10.5.1
RUN npm ci
COPY ./frontend ./
RUN npm run make-i18n && npm run build
@@ -30,25 +32,44 @@ FROM python:3.12-slim as runtime
WORKDIR /app
ENV RUN_AS_DEVIN=false
ENV RUN_AS_DEVIN=true
ENV OPENDEVIN_USER_ID=1000
ENV USE_HOST_NETWORK=false
ENV SSH_HOSTNAME=host.docker.internal
ENV WORKSPACE_BASE=/opt/workspace_base
ENV OPEN_DEVIN_BUILD_VERSION=$OPEN_DEVIN_BUILD_VERSION
RUN mkdir -p $WORKSPACE_BASE
RUN apt-get update -y \
&& apt-get install -y curl ssh
&& apt-get install -y curl ssh sudo
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: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}
COPY --chown=opendevin:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
RUN playwright install --with-deps chromium
COPY ./opendevin ./opendevin
COPY ./agenthub ./agenthub
RUN python opendevin/download.py # No-op to download assets
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
COPY --from=frontend-builder /app/dist ./frontend/dist
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
CMD ["uvicorn", "opendevin.server.listen:app", "--host", "0.0.0.0", "--port", "3000"]
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
CMD ["/app/entrypoint.sh"]

View File

@@ -1,2 +1,4 @@
DOCKER_REPOSITORY=ghcr.io/opendevin/opendevin
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_IMAGE=opendevin
DOCKER_BASE_DIR="."

26
containers/app/entrypoint.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# check user is root
if [ "$(id -u)" -ne 0 ]; then
echo "The OpenDevin entrypoint.sh must run as root"
exit 1
fi
if [ -z "$SANDBOX_USER_ID" ]; then
echo "SANDBOX_USER_ID is not set"
exit 1
fi
# change uid of opendevin user to match the host user
# but the group id is not changed, so the user can still access everything under /app
useradd -l -m -u $SANDBOX_USER_ID -s /bin/bash enduser
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 enduser
# switch to the user and start the server
su enduser -c "cd /app && uvicorn opendevin.server.listen:app --host 0.0.0.0 --port 3000"

View File

@@ -2,21 +2,30 @@
set -eo pipefail
image_name=$1
org_name=$2
push=0
if [[ $2 == "--push" ]]; then
if [[ $3 == "--push" ]]; then
push=1
fi
echo -e "Building: $image_name"
tags=(latest)
tags=()
OPEN_DEVIN_BUILD_VERSION="dev"
cache_tag_base="buildcache"
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)
fi
sanitized=$(echo $GITHUB_REF_NAME | sed 's/[^a-zA-Z0-9.-]\+/-/g')
OPEN_DEVIN_BUILD_VERSION=$sanitized
cache_tag+="-${sanitized}"
tags+=($sanitized)
fi
echo "Tags: ${tags[@]}"
@@ -31,18 +40,28 @@ if [ ! -f $dir/config.sh ]; then
exit 1
fi
source $dir/config.sh
if [[ -n "$org_name" ]]; then
DOCKER_ORG="$org_name"
fi
DOCKER_REPOSITORY=$DOCKER_REGISTRY/$DOCKER_ORG/$DOCKER_IMAGE
DOCKER_REPOSITORY=${DOCKER_REPOSITORY,,} # lowercase
echo "Repo: $DOCKER_REPOSITORY"
echo "Base dir: $DOCKER_BASE_DIR"
#docker pull $DOCKER_REPOSITORY:main || true # try to get any cached layers
args=""
for tag in ${tags[@]}; do
args+=" -t $DOCKER_REPOSITORY:$tag"
done
if [[ $push -eq 1 ]]; then
args+=" --push"
args+=" --cache-to=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag,mode=max"
fi
docker buildx build \
$args \
--build-arg OPEN_DEVIN_BUILD_VERSION=$OPEN_DEVIN_BUILD_VERSION \
--cache-from=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag \
--cache-from=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag_base-main \
--platform linux/amd64,linux/arm64 \
--provenance=false \
-f $dir/Dockerfile $DOCKER_BASE_DIR

View File

@@ -0,0 +1,19 @@
FROM ubuntu:22.04
# install basic packages
RUN apt-get update && apt-get install -y \
curl \
wget \
git \
vim \
nano \
unzip \
zip \
python3 \
python3-pip \
python3-venv \
python3-dev \
build-essential \
openssh-server \
sudo \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -0,0 +1,15 @@
# How to build custom E2B sandbox for OpenDevin
[E2B](https://e2b.dev) is an [open-source](https://github.com/e2b-dev/e2b) secure cloud environment (sandbox) made for running AI-generated code and agents. E2B offers [Python](https://pypi.org/project/e2b/) and [JS/TS](https://www.npmjs.com/package/e2b) SDK to spawn and control these sandboxes.
1. Install the CLI with NPM.
```sh
npm install -g @e2b/cli@latest
```
Full CLI API is [here](https://e2b.dev/docs/cli/installation).
1. Build the sandbox
```sh
e2b template build --dockerfile ./Dockerfile --name "open-devin"
```

View File

@@ -0,0 +1,14 @@
# This is a config for E2B sandbox template.
# You can use 'template_id' (785n69crgahmz0lkdw9h) or 'template_name (open-devin) from this config to spawn a sandbox:
# Python SDK
# from e2b import Sandbox
# sandbox = Sandbox(template='open-devin')
# JS SDK
# import { Sandbox } from 'e2b'
# const sandbox = await Sandbox.create({ template: 'open-devin' })
dockerfile = "Dockerfile"
template_name = "open-devin"
template_id = "785n69crgahmz0lkdw9h"

View File

@@ -1,2 +1,4 @@
DOCKER_REPOSITORY=ghcr.io/opendevin/eval-swe-bench
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_IMAGE=eval-swe-bench
DOCKER_BASE_DIR=evaluation/SWE-bench

View File

@@ -16,6 +16,18 @@ RUN apt-get update && apt-get install -y \
build-essential \
openssh-server \
sudo \
bash \
gcc \
jq \
g++ \
make \
&& rm -rf /var/lib/apt/lists/*
RUN service ssh start
RUN mkdir -p -m0755 /var/run/sshd
# symlink python3 to python
RUN ln -s /usr/bin/python3 /usr/bin/python
# install basic dependencies for CodeActAgent
RUN pip3 install --upgrade pip
RUN pip3 install jupyterlab notebook jupyter_kernel_gateway flake8

View File

@@ -1,2 +1,4 @@
DOCKER_REPOSITORY=ghcr.io/opendevin/sandbox
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_IMAGE=sandbox
DOCKER_BASE_DIR="."

View File

@@ -1,44 +1,43 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
exclude: docs/modules/python
- id: end-of-file-fixer
exclude: docs/modules/python
- id: check-yaml
- id: debug-statements
- id: double-quote-string-fixer
- repo: https://github.com/hhatto/autopep8
rev: v2.1.0
- repo: https://github.com/tox-dev/pyproject-fmt
rev: 1.7.0
hooks:
- id: autopep8
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.5.0
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.16
hooks:
- id: setup-cfg-fmt
always_run: true
pass_filenames: false
- id: validate-pyproject
- 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/
types_or: [ python, pyi, jupyter ]
args: [ --fix ]
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/
types_or: [ python, pyi, jupyter ]
entry: ruff format --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
additional_dependencies: [types-requests, types-setuptools, types-pyyaml, types-toml]
additional_dependencies:
[types-requests, types-setuptools, types-pyyaml, types-toml]
entry: mypy --config-file dev_config/python/mypy.ini opendevin/ agenthub/
always_run: true
pass_filenames: false

View File

@@ -1,3 +1,23 @@
exclude = [
"agenthub/monologue_agent/regression/",
]
]
[lint]
select = [
"E",
"W",
"F",
"I",
"Q",
]
ignore = [
"E501",
]
[lint.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "single"
[format]
quote-style = "single"

20
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

41
docs/README.md Normal file
View File

@@ -0,0 +1,41 @@
# Website
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
### Installation
```
$ yarn
```
### Local Development
```
$ yarn start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build
```
$ yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
Using SSH:
```
$ USE_SSH=true yarn deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

View File

@@ -1,14 +0,0 @@
# System Architecture Overview
This is a high-level overview of the system architecture. The system is divided into two main components: the frontend and the backend. The frontend is responsible for handling user interactions and displaying the results. The backend is responsible for handling the business logic and executing the agents.
![system_architecture.svg](system_architecture.svg)
This Overview is simplified to show the main components and their interactions. For a more detailed view of the backend architecture, see the [Backend Architecture](#backend-architecture) section.
# Backend Architecture
*__Disclaimer__: The backend architecture is a work in progress and is subject to change. The following diagram shows the current architecture of the backend based on the commit that is shown in the footer of the diagram.*
![backend_architecture.svg](backend_architecture.svg)

View File

@@ -1,23 +0,0 @@
# Process for updating the backend architecture diagram
The generation of the backend architecture diagram is partially automated. The diagram is generated from the type hints in the code using the py2puml tool. The diagram is then manually reviewed, adjusted and exported to PNG and SVG.
## Prerequisites
- Running python environment in which opendevin is executable (according to the instructions in the README.md file in the root of the repository)
- [py2puml](https://github.com/lucsorel/py2puml) installed
## Steps
1. Autogenerate the diagram by running the following command from the root of the repository:
```py2puml opendevin opendevin > docs/architecture/backend_architecture.puml```
2. Open the generated file in a PlantUML editor, e.g. Visual Studio Code with the PlantUML extension or [PlantText](https://www.planttext.com/)
3. Review the generated PUML and make all necessary adjustments to the diagram (add missing parts, fix mistakes, improve positioning).
*py2puml creates the diagram based on the type hints in the code, so missing or incorrect type hints may result in an incomplete or incorrect diagram.*
4. Review the diff between the new and the previous diagram and manually check if the changes are correct.
*Make sure not to remove parts that were manually added to the diagram in the past and are still relevant.*
4. Add the commit hash of the commit that was used to generate the diagram to the diagram footer.
5. Export the diagram as PNG and SVG files and replace the existing diagrams in the `docs/architecture` directory. This can be done with (e.g. [PlantText](https://www.planttext.com/))

3
docs/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@@ -1,96 +0,0 @@
# Agents and Capabilities
## Monologue Agent:
### Description:
The Monologue Agent utilizes long and short term memory to complete tasks.
Long term memory is stored as a LongTermMemory object and the model uses it to search for examples from the past.
Short term memory is stored as a Monologue object and the model can condense it as necessary.
### Actions:
`Action`,
`NullAction`,
`CmdRunAction`,
`FileWriteAction`,
`FileReadAction`,
`AgentRecallAction`,
`BrowseURLAction`,
`AgentThinkAction`
### Observations:
`Observation`,
`NullObservation`,
`CmdOutputObservation`,
`FileReadObservation`,
`AgentRecallObservation`,
`BrowserOutputObservation`
### Methods:
`__init__`: Initializes the agent with a long term memory, and an internal monologue
`_add_event`: Appends events to the monologue of the agent and condenses with summary automatically if the monologue is too long
`_initialize`: Utilizes the `INITIAL_THOUGHTS` list to give the agent a context for its capabilities and how to navigate the `/workspace`
`step`: Modifies the current state by adding the most rescent actions and observations, then prompts the model to think about its next action to take.
`search_memory`: Uses `VectorIndexRetriever` to find related memories within the long term memory.
## Planner Agent:
### Description:
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
### Actions:
`NullAction`,
`CmdRunAction`,
`CmdKillAction`,
`BrowseURLAction`,
`FileReadAction`,
`FileWriteAction`,
`AgentRecallAction`,
`AgentThinkAction`,
`AgentFinishAction`,
`AgentSummarizeAction`,
`AddTaskAction`,
`ModifyTaskAction`,
### Observations:
`Observation`,
`NullObservation`,
`CmdOutputObservation`,
`FileReadObservation`,
`AgentRecallObservation`,
`BrowserOutputObservation`
### Methods:
`__init__`: Initializes an agent with `llm`
`step`: Checks to see if current step is completed, returns `AgentFinishAction` if True. Otherwise, creates a plan prompt and sends to model for inference, adding the result as the next action.
`search_memory`: Not yet implemented
## CodeAct Agent:
### Description:
The Code Act Agent is a minimalist agent. The agent works by passing the model a list of action-observation pairs and prompting the model to take the next step.
### Actions:
`Action`,
`CmdRunAction`,
`AgentEchoAction`,
`AgentFinishAction`,
### Observations:
`CmdOutputObservation`,
`AgentMessageObservation`,
### Methods:
`__init__`: Initializes an agent with `llm` and a list of messages `List[Mapping[str, str]]`
`step`: First, gets messages from state and then compiles them into a list for context. Next, pass the context list with the prompt to get the next command to execute. Finally, Execute command if valid, else return `AgentEchoAction(INVALID_INPUT_MESSAGE)`
`search_memory`: Not yet implemented

View File

@@ -1,108 +0,0 @@
# Local LLM Guide with Ollama server
## 0. Install Ollama:
run the following command in a conda env with CUDA etc.
Linux:
```
curl -fsSL https://ollama.com/install.sh | sh
```
Windows or macOS:
- Download from [here](https://ollama.com/download/)
## 1. Install Models:
Ollama model names can be found [here](https://ollama.com/library) (See example below)
![alt text](images/ollama.png)
Once you have found the model you want to use copy the command and run it in your conda env.
Example of llama2 q4 quantized:
```
conda activate <env_name>
ollama run llama2:13b-chat-q4_K_M
```
you can check which models you have downloaded like this:
```
~$ ollama list
NAME ID SIZE MODIFIED
llama2:latest 78e26419b446 3.8 GB 6 weeks ago
mistral:7b-instruct-v0.2-q4_K_M eb14864c7427 4.4 GB 2 weeks ago
starcoder2:latest f67ae0f64584 1.7 GB 19 hours ago
```
## 2. Run Ollama in CLI:
This command starts up the ollama server that is on port `11434`
This will show the requests in CLI
```
conda activate <env_name>
ollama serve
```
or
This will run with no output in the background
```
sudo systemctl start ollama
```
If you see something like this:
```
Error: listen tcp 127.0.0.1:11434: bind: address already in use
```
This is not an error it just means the server is already running
To stop the server use:
```
sudo systemctl stop ollama
```
For more info go [here](https://github.com/ollama/ollama/blob/main/docs/faq.md)
## 3. Start OpenDevin
Use the instructions in [README.md](/README.md) to start OpenDevin using Docker.
When running `docker run`, add the following environment variables using `-e`:
```
LLM_API_KEY="ollama"
LLM_MODEL="ollama/<model_name>"
LLM_EMBEDDING_MODEL="local"
LLM_BASE_URL="http://localhost:<port_number>"
WORKSPACE_DIR="./workspace"
```
Notes:
- The API key should be set to `"ollama"`
- The base url needs to be `localhost`
- By default ollama port is `11434` unless you set it
- `model_name` needs to be the entire model name
- Example: `LLM_MODEL="ollama/llama2:13b-chat-q4_K_M"`
You should now be able to connect to `http://localhost:3001/` with your local model running!
## Additional Notes for WSL2 Users:
1. If you encounter the following error during setup: `Exception: Failed to create opendevin user in sandbox: b'useradd: UID 0 is not unique\n'`
You can resolve it by running:
```
export SANDBOX_USER_ID=1000
```
2. If you face issues running Poetry even after installing it during the build process, you may need to add its binary path to your environment:
```
export PATH="$HOME/.local/bin:$PATH"
```
3. If you experiencing issues related to networking, such as `NoneType object has no attribute 'request'` when executing `make run`, you may need to configure your WSL2 networking settings. Follow these steps:
- Open or create the `.wslconfig` file located at `C:\Users\%username%\.wslconfig` on your Windows host machine.
- Add the following configuration to the `.wslconfig` file:
```
[wsl2]
networkingMode=mirrored
localhostForwarding=true
```
- Save the `.wslconfig` file.
- Restart WSL2 completely by exiting any running WSL2 instances and executing the command `wsl --shutdown` in your command prompt or terminal.
- After restarting WSL, attempt to execute `make run` again. The networking issue should be resolved.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

128
docs/docusaurus.config.ts Normal file
View File

@@ -0,0 +1,128 @@
import type * as Preset from "@docusaurus/preset-classic";
import type { Config } from "@docusaurus/types";
import { themes as prismThemes } from "prism-react-renderer";
const config: Config = {
title: "OpenDevin",
tagline: "Code Less, Make More",
favicon: "img/logo.png",
// Set the production url of your site here
url: "https://OpenDevin.github.io",
baseUrl: "/OpenDevin/",
// GitHub pages deployment config.
organizationName: "OpenDevin",
projectName: "OpenDevin",
trailingSlash: false,
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
// may want to replace "en" with "zh-Hans".
i18n: {
defaultLocale: "en",
locales: ["en"],
},
presets: [
[
"classic",
{
docs: {
path: "modules",
routeBasePath: "modules",
sidebarPath: "./sidebars.ts",
exclude: [
// '**/_*.{js,jsx,ts,tsx,md,mdx}',
// '**/_*/**',
"**/*.test.{js,jsx,ts,tsx}",
"**/__tests__/**",
],
},
blog: {
showReadingTime: true,
},
theme: {
customCss: "./src/css/custom.css",
},
} satisfies Preset.Options,
],
],
themeConfig: {
image: "img/docusaurus.png",
navbar: {
title: "OpenDevin",
logo: {
alt: "OpenDevin",
src: "img/logo.png",
},
items: [
{
type: "docSidebar",
sidebarId: "docsSidebar",
position: "left",
label: "Docs",
},
{
type: "docSidebar",
sidebarId: "apiSidebar",
position: "left",
label: "Codebase",
},
{ to: "/faq", label: "FAQ", position: "left" },
{
href: "https://github.com/OpenDevin/OpenDevin",
label: "GitHub",
position: "right",
},
],
},
footer: {
style: "dark",
links: [
{
title: "OpenDevin",
items: [
{
label: "Docs",
to: "/modules/usage/intro",
},
],
},
{
title: "Community",
items: [
{
label: "Slack",
href: "https://join.slack.com/t/opendevin/shared_invite/zt-2ggtwn3k5-PvAA2LUmqGHVZ~XzGq~ILw"
},
{
label: "Discord",
href: "https://discord.gg/ESHStjSjD4",
},
],
},
{
title: "More",
items: [
{
label: "GitHub",
href: "https://github.com/OpenDevin/OpenDevin",
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} OpenDevin`,
},
prism: {
theme: prismThemes.oneLight,
darkTheme: prismThemes.oneDark,
},
} satisfies Preset.ThemeConfig,
};
export default config;

View File

@@ -0,0 +1,3 @@
# Python Docs
Docs will appear here after deployment.

View File

@@ -0,0 +1,5 @@
{
"items": ["python/python"],
"label": "Backend",
"type": "category"
}

View File

@@ -0,0 +1,53 @@
---
sidebar_position: 6
---
# 📚 Misc
## ⭐️ Research Strategy
Achieving full replication of production-grade applications with LLMs is a complex endeavor. Our strategy involves:
1. **Core Technical Research:** Focusing on foundational research to understand and improve the technical aspects of code generation and handling.
2. **Specialist Abilities:** Enhancing the effectiveness of core components through data curation, training methods, and more.
3. **Task Planning:** Developing capabilities for bug detection, codebase management, and optimization.
4. **Evaluation:** Establishing comprehensive evaluation metrics to better understand and improve our models.
## 🚧 Default Agent
- Our default Agent is currently the MonologueAgent, which has limited capabilities, but is fairly stable. We're working on other Agent implementations, including [SWE Agent](https://swe-agent.com/). You can [read about our current set of agents here](./agents).
## 🤝 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:
- **Code Contributions:** Help us develop the core functionalities, frontend interface, or sandboxing solutions.
- **Research and Evaluation:** Contribute to our understanding of LLMs in software engineering, participate in evaluating the models, or suggest improvements.
- **Feedback and Testing:** Use the OpenDevin toolset, report bugs, suggest features, or provide feedback on usability.
For details, please check [this document](https://github.com/OpenDevin/OpenDevin/blob/main/CONTRIBUTING.md).
## 🤖 Join Our Community
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-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!
🐚 **Code less, make more with OpenDevin.**
[![Star History Chart](https://api.star-history.com/svg?repos=OpenDevin/OpenDevin&type=Date)](https://star-history.com/#OpenDevin/OpenDevin&Date)
## 🛠️ Built With
OpenDevin is built using a combination of powerful frameworks and libraries, providing a robust foundation for its development. Here are the key technologies used in the project:
![FastAPI](https://img.shields.io/badge/FastAPI-black?style=for-the-badge) ![uvicorn](https://img.shields.io/badge/uvicorn-black?style=for-the-badge) ![LiteLLM](https://img.shields.io/badge/LiteLLM-black?style=for-the-badge) ![Docker](https://img.shields.io/badge/Docker-black?style=for-the-badge) ![Ruff](https://img.shields.io/badge/Ruff-black?style=for-the-badge) ![MyPy](https://img.shields.io/badge/MyPy-black?style=for-the-badge) ![LlamaIndex](https://img.shields.io/badge/LlamaIndex-black?style=for-the-badge) ![React](https://img.shields.io/badge/React-black?style=for-the-badge)
Please note that the selection of these technologies is in progress, and additional technologies may be added or existing ones may be removed as the project evolves. We strive to adopt the most suitable and efficient tools to enhance the capabilities of OpenDevin.
## 📜 License
Distributed under the MIT License. See [our license](https://github.com/OpenDevin/OpenDevin/blob/main/LICENSE) for more information.

View File

@@ -0,0 +1,142 @@
---
sidebar_position: 3
---
# 🧠 Agents and Capabilities
## CodeAct Agent
### Description
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&#x27;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)*
### Actions
`Action`,
`CmdRunAction`,
`IPythonRunCellAction`,
`AgentEchoAction`,
`AgentFinishAction`,
`AgentTalkAction`
### Observations
`CmdOutputObservation`,
`IPythonRunCellObservation`,
`AgentMessageObservation`,
`UserMessageObservation`
### Methods
| Method | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `__init__` | Initializes an agent with `llm` and a list of messages `List[Mapping[str, str]]` |
| `step` | Performs one step using the CodeAct Agent. This includes gathering info on previous steps and prompting the model to make a command to execute. |
| `search_memory` | Not yet implemented |
### Work-in-progress &amp; Next step
[] Support web-browsing
[] Complete the workflow for CodeAct agent to submit Github PRs
## Monologue Agent
### Description
The Monologue Agent utilizes long and short term memory to complete tasks.
Long term memory is stored as a LongTermMemory object and the model uses it to search for examples from the past.
Short term memory is stored as a Monologue object and the model can condense it as necessary.
### Actions
`Action`,
`NullAction`,
`CmdRunAction`,
`FileWriteAction`,
`FileReadAction`,
`AgentRecallAction`,
`BrowseURLAction`,
`GithubPushAction`,
`AgentThinkAction`
### Observations
`Observation`,
`NullObservation`,
`CmdOutputObservation`,
`FileReadObservation`,
`AgentRecallObservation`,
`BrowserOutputObservation`
### Methods
| Method | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `__init__` | Initializes the agent with a long term memory, and an internal monologue |
| `_add_event` | Appends events to the monologue of the agent and condenses with summary automatically if the monologue is too long |
| `_initialize` | Utilizes the `INITIAL_THOUGHTS` list to give the agent a context for its capabilities and how to navigate the `/workspace` |
| `step` | Modifies the current state by adding the most recent actions and observations, then prompts the model to think about its next action to take. |
| `search_memory` | Uses `VectorIndexRetriever` to find related memories within the long term memory. |
## Planner Agent
### Description
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
### Actions
`NullAction`,
`CmdRunAction`,
`CmdKillAction`,
`BrowseURLAction`,
`GithubPushAction`,
`FileReadAction`,
`FileWriteAction`,
`AgentRecallAction`,
`AgentThinkAction`,
`AgentFinishAction`,
`AgentSummarizeAction`,
`AddTaskAction`,
`ModifyTaskAction`,
### Observations
`Observation`,
`NullObservation`,
`CmdOutputObservation`,
`FileReadObservation`,
`AgentRecallObservation`,
`BrowserOutputObservation`
### Methods
| Method | Description |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `__init__` | Initializes an agent with `llm` |
| `step` | Checks to see if current step is completed, returns `AgentFinishAction` if True. Otherwise, creates a plan prompt and sends to model for inference, adding the result as the next action. |
| `search_memory` | Not yet implemented |

View File

@@ -0,0 +1,51 @@
---
sidebar_position: 4
---
# 🏛️ System Architecture Overview
This is a high-level overview of the system architecture. The system is divided into two main components: the frontend and the backend. The frontend is responsible for handling user interactions and displaying the results. The backend is responsible for handling the business logic and executing the agents.
![system_architecture.svg](/img/system_architecture.svg)
This Overview is simplified to show the main components and their interactions. For a more detailed view of the backend architecture, see the [Backend Architecture](#backend-architecture) section.
# Backend Architecture
_**Disclaimer**: The backend architecture is a work in progress and is subject to change. The following diagram shows the current architecture of the backend based on the commit that is shown in the footer of the diagram._
![backend_architecture.svg](/img/backend_architecture.svg)
<details>
<summary>Updating this Diagram</summary>
<div>
The generation of the backend architecture diagram is partially automated.
The diagram is generated from the type hints in the code using the py2puml
tool. The diagram is then manually reviewed, adjusted and exported to PNG
and SVG.
## Prerequisites
- Running python environment in which opendevin is executable
(according to the instructions in the README.md file in the root of the repository)
- [py2puml](https://github.com/lucsorel/py2puml) installed
## Steps
1. Autogenerate the diagram by running the following command from the root of the repository:
`py2puml opendevin opendevin > docs/architecture/backend_architecture.puml`
2. Open the generated file in a PlantUML editor, e.g. Visual Studio Code with the PlantUML extension or [PlantText](https://www.planttext.com/)
3. Review the generated PUML and make all necessary adjustments to the diagram (add missing parts, fix mistakes, improve positioning).
_py2puml creates the diagram based on the type hints in the code, so missing or incorrect type hints may result in an incomplete or incorrect diagram._
4. Review the diff between the new and the previous diagram and manually check if the changes are correct.
_Make sure not to remove parts that were manually added to the diagram in the past and are still relevant._
5. Add the commit hash of the commit that was used to generate the diagram to the diagram footer.
6. Export the diagram as PNG and SVG files and replace the existing diagrams in the `docs/architecture` directory. This can be done with (e.g. [PlantText](https://www.planttext.com/))
</div>
</details>

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