mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72efa05c71 | ||
|
|
322b23550d | ||
|
|
1d92e9a27b | ||
|
|
6013faeec5 | ||
|
|
bccb8297b8 | ||
|
|
b2a99b509f | ||
|
|
7d8e10aa18 | ||
|
|
74e159add6 | ||
|
|
91c8f1cb3f | ||
|
|
7d2d2ab242 | ||
|
|
3cdff79173 | ||
|
|
3886c51217 | ||
|
|
2c3fd1322d | ||
|
|
99e4dd1730 | ||
|
|
d9ba45dae8 | ||
|
|
04306b0219 | ||
|
|
7914258945 | ||
|
|
fadcdc117e | ||
|
|
ce7c7eaae4 | ||
|
|
f19333aa6e | ||
|
|
1e50d58982 | ||
|
|
24750ba04f | ||
|
|
cc4879a973 | ||
|
|
11d8253215 | ||
|
|
0bdbd8a90d | ||
|
|
8dbbbcf1ba | ||
|
|
30e10e7a4a | ||
|
|
95e4ca490f | ||
|
|
836864fa88 | ||
|
|
0d77f495e3 | ||
|
|
24cc8ceb96 | ||
|
|
1b810cfbf0 | ||
|
|
cfef3ee5c4 | ||
|
|
435f47ca0e | ||
|
|
545327cc1e | ||
|
|
c7dd443fa2 | ||
|
|
c50319138e | ||
|
|
d80f025e21 | ||
|
|
442ab7371c | ||
|
|
1c7cdbefdd | ||
|
|
ea214d1c07 | ||
|
|
62e4fb47b2 | ||
|
|
5f2d6f58f2 | ||
|
|
0cda5f64af | ||
|
|
eb1c3d8790 | ||
|
|
fe43aeb9b6 | ||
|
|
ccbbabac1c | ||
|
|
eb7703a44e | ||
|
|
31c1a2d748 |
2
.github/ISSUE_TEMPLATE/bug_template.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_template.yml
vendored
@@ -12,7 +12,7 @@ body:
|
||||
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://github.com/OpenDevin/OpenDevin/blob/main/docs/guides/Troubleshooting.md
|
||||
- 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
|
||||
|
||||
13
.github/workflows/deploy-docs.yml
vendored
13
.github/workflows/deploy-docs.yml
vendored
@@ -12,9 +12,6 @@ jobs:
|
||||
build:
|
||||
name: Build Docusaurus
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: docs
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -24,11 +21,17 @@ jobs:
|
||||
node-version: 18
|
||||
cache: npm
|
||||
cache-dependency-path: docs/package-lock.json
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Generate Python Docs
|
||||
run: rm -rf docs/modules/python && pip install pydoc-markdown && pydoc-markdown
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: cd docs && npm ci
|
||||
- name: Build website
|
||||
run: npm run build
|
||||
run: cd docs && npm run build
|
||||
|
||||
- name: Upload Build Artifact
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
30
.github/workflows/dummy-agent-test.yml
vendored
Normal file
30
.github/workflows/dummy-agent-test.yml
vendored
Normal 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
|
||||
29
.github/workflows/ghcr.yml
vendored
29
.github/workflows/ghcr.yml
vendored
@@ -1,9 +1,15 @@
|
||||
name: Publish Docker Image
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -28,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
|
||||
|
||||
@@ -42,16 +64,13 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Delete huge unnecessary tools folder
|
||||
run: rm -rf /opt/hostedtoolcache
|
||||
|
||||
- name: Build and push ${{ matrix.image }}
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
if: "!github.event.pull_request.head.repo.fork"
|
||||
run: |
|
||||
./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }} --push
|
||||
|
||||
- name: Build ${{ matrix.image }}
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
if: "github.event.pull_request.head.repo.fork"
|
||||
run: |
|
||||
./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }}
|
||||
|
||||
|
||||
11
.github/workflows/lint.yml
vendored
11
.github/workflows/lint.yml
vendored
@@ -1,5 +1,9 @@
|
||||
name: Lint
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -33,6 +37,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
@@ -41,8 +47,11 @@ jobs:
|
||||
- name: Install pre-commit
|
||||
run: pip install pre-commit==3.7.0
|
||||
- name: Run pre-commit hooks
|
||||
if: github.ref != 'refs/heads/main'
|
||||
run: |
|
||||
git fetch https://github.com/OpenDevin/OpenDevin.git main:main && \
|
||||
pre-commit run \
|
||||
--all-files \
|
||||
--files \
|
||||
$(git diff --name-only $(git merge-base main $(git branch --show-current)) $(git branch --show-current) | tr '\n' ' ') \
|
||||
--show-diff-on-failure \
|
||||
--config ./dev_config/python/.pre-commit-config.yaml
|
||||
|
||||
2
.github/workflows/review-pr.yml
vendored
2
.github/workflows/review-pr.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
SANDBOX_TYPE: exec
|
||||
run: |
|
||||
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
|
||||
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
|
||||
rm task.txt
|
||||
|
||||
- name: Check if review file is non-empty
|
||||
|
||||
4
.github/workflows/run-integration-tests.yml
vendored
4
.github/workflows/run-integration-tests.yml
vendored
@@ -1,5 +1,9 @@
|
||||
name: Run Integration Tests
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
||||
10
.github/workflows/run-unit-tests.yml
vendored
10
.github/workflows/run-unit-tests.yml
vendored
@@ -1,5 +1,9 @@
|
||||
name: Run Unit Tests
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -34,11 +38,16 @@ jobs:
|
||||
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:
|
||||
@@ -70,6 +79,7 @@ jobs:
|
||||
|
||||
- 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:
|
||||
|
||||
2
.github/workflows/solve-issue.yml
vendored
2
.github/workflows/solve-issue.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
SANDBOX_TYPE: exec
|
||||
run: |
|
||||
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
|
||||
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
|
||||
rm task.txt
|
||||
|
||||
- name: Setup Git, Create Branch, and Commit Changes
|
||||
|
||||
@@ -82,3 +82,15 @@ If you encounter any issues with the Language Model (LM) or you're simply curiou
|
||||
```bash
|
||||
make help
|
||||
```
|
||||
|
||||
### 8. Testing
|
||||
|
||||
#### Unit tests
|
||||
|
||||
```bash
|
||||
poetry run pytest ./tests/unit/test_sandbox.py
|
||||
```
|
||||
|
||||
#### Integration tests
|
||||
|
||||
Please refer to [this README](./tests/integration/README.md) for details.
|
||||
|
||||
21
Makefile
21
Makefile
@@ -148,9 +148,24 @@ install-precommit-hooks:
|
||||
@poetry run pre-commit install --config $(PRECOMMIT_CONFIG_PATH)
|
||||
@echo "$(GREEN)Pre-commit hooks installed successfully.$(RESET)"
|
||||
|
||||
lint:
|
||||
lint-backend:
|
||||
@echo "$(YELLOW)Running linters...$(RESET)"
|
||||
@poetry run pre-commit run --all-files --show-diff-on-failure --config $(PRECOMMIT_CONFIG_PATH)
|
||||
@poetry run pre-commit run --files $$(git diff --name-only $$(git merge-base main $$(git branch --show-current)) $$(git branch --show-current) | tr '\n' ' ') --show-diff-on-failure --config $(PRECOMMIT_CONFIG_PATH)
|
||||
|
||||
lint-frontend:
|
||||
@echo "$(YELLOW)Running linters for frontend...$(RESET)"
|
||||
@cd frontend && npm run lint
|
||||
|
||||
lint:
|
||||
@$(MAKE) -s lint-frontend
|
||||
@$(MAKE) -s lint-backend
|
||||
|
||||
test-frontend:
|
||||
@echo "$(YELLOW)Running tests for frontend...$(RESET)"
|
||||
@cd frontend && npm run test
|
||||
|
||||
test:
|
||||
@$(MAKE) -s test-frontend
|
||||
|
||||
build-frontend:
|
||||
@echo "$(YELLOW)Building frontend...$(RESET)"
|
||||
@@ -159,7 +174,7 @@ build-frontend:
|
||||
# Start backend
|
||||
start-backend:
|
||||
@echo "$(YELLOW)Starting backend...$(RESET)"
|
||||
@poetry run uvicorn opendevin.server.listen:app --port $(BACKEND_PORT) --reload --reload-exclude workspace/*
|
||||
@poetry run uvicorn opendevin.server.listen:app --port $(BACKEND_PORT) --reload --reload-exclude "workspace/*"
|
||||
|
||||
# Start frontend
|
||||
start-frontend:
|
||||
|
||||
@@ -24,14 +24,15 @@
|
||||
<a href="https://github.com/OpenDevin/OpenDevin/issues"><img src="https://img.shields.io/github/issues/opendevin/opendevin?style=for-the-badge" alt="Issues"></a>
|
||||
<a href="https://github.com/OpenDevin/OpenDevin/blob/main/LICENSE"><img src="https://img.shields.io/github/license/opendevin/opendevin?style=for-the-badge" alt="MIT License"></a>
|
||||
</br>
|
||||
<a href="https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="Join our Slack community"></a>
|
||||
<a href="https://discord.gg/mBuDGRzzES"><img src="https://img.shields.io/badge/Discord-Join%20Us-purple?logo=discord&logoColor=white&style=for-the-badge" alt="Join our Discord community"></a>
|
||||
<a href="https://join.slack.com/t/opendevin/shared_invite/zt-2i1iqdag6-bVmvamiPA9EZUu7oCO6KhA"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="Join our Slack community"></a>
|
||||
<a href="https://discord.gg/ESHStjSjD4"><img src="https://img.shields.io/badge/Discord-Join%20Us-purple?logo=discord&logoColor=white&style=for-the-badge" alt="Join our Discord community"></a>
|
||||
</div>
|
||||
|
||||
<!-- PROJECT LOGO -->
|
||||
<div align="center">
|
||||
<img src="./docs/static/img/logo.png" alt="Logo" width="200" height="200">
|
||||
<h1 align="center">OpenDevin: Code Less, Make More</h1>
|
||||
<a href="https://opendevin.github.io/OpenDevin/"><img src="https://img.shields.io/badge/Documenation-OpenDevin-blue?logo=googledocs&logoColor=white&style=for-the-badge" alt="Check out the documentation"></a>
|
||||
</div>
|
||||
|
||||
## 🎯 Mission
|
||||
@@ -66,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!
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .agent import SWEAgent
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
from typing import List
|
||||
|
||||
from opendevin.action import (
|
||||
from opendevin.controller.agent import Agent
|
||||
from opendevin.controller.state.state import State
|
||||
from opendevin.events.action import (
|
||||
Action,
|
||||
AgentThinkAction,
|
||||
FileReadAction,
|
||||
FileWriteAction,
|
||||
)
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.events.observation import Observation
|
||||
from opendevin.llm.llm import LLM
|
||||
from opendevin.observation import Observation
|
||||
from opendevin.state import State
|
||||
|
||||
from .parser import parse_command
|
||||
from .prompts import (
|
||||
@@ -48,9 +48,12 @@ class SWEAgent(Agent):
|
||||
)
|
||||
action_resp = resp['choices'][0]['message']['content']
|
||||
print(f"\033[1m\033[91m{resp['usage']}\033[0m")
|
||||
print('\n==== RAW OUTPUT ====',
|
||||
f'\033[96m{action_resp}\033[0m',
|
||||
'==== END RAW ====\n', sep='\n')
|
||||
print(
|
||||
'\n==== RAW OUTPUT ====',
|
||||
f'\033[96m{action_resp}\033[0m',
|
||||
'==== END RAW ====\n',
|
||||
sep='\n',
|
||||
)
|
||||
return parse_command(action_resp, self.cur_file, self.cur_line)
|
||||
|
||||
def _update(self, action: Action) -> None:
|
||||
@@ -68,22 +71,15 @@ class SWEAgent(Agent):
|
||||
for prev_action, obs in state.updated_info:
|
||||
self._remember(prev_action, obs)
|
||||
|
||||
prompt = STEP_PROMPT(
|
||||
state.plan.main_goal,
|
||||
self.cur_file,
|
||||
self.cur_line
|
||||
)
|
||||
prompt = STEP_PROMPT(state.plan.main_goal, self.cur_file, self.cur_line)
|
||||
|
||||
msgs = [
|
||||
{'content': SYSTEM_MESSAGE, 'role': 'system'},
|
||||
{'content': prompt, 'role': 'user'}
|
||||
{'content': prompt, 'role': 'user'},
|
||||
]
|
||||
|
||||
if len(self.running_memory) > 0:
|
||||
context = CONTEXT_PROMPT(
|
||||
self.running_memory,
|
||||
self.memory_window
|
||||
)
|
||||
context = CONTEXT_PROMPT(self.running_memory, self.memory_window)
|
||||
msgs.insert(1, {'content': context, 'role': 'user'})
|
||||
# clrs = [''] * (len(msgs)-2) + ['\033[0;36m', '\033[0;35m']
|
||||
# print('\n\n'.join([c+m['content']+'\033[0m' for c, m in zip(clrs, msgs)]))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import re
|
||||
|
||||
from opendevin.action import (
|
||||
from opendevin.events.action import (
|
||||
Action,
|
||||
AgentEchoAction,
|
||||
AgentFinishAction,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .micro.agent import MicroAgent
|
||||
from .micro.registry import all_microagents
|
||||
@@ -8,25 +8,35 @@ from .micro.registry import all_microagents
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# Import agents after environment variables are loaded
|
||||
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']
|
||||
__all__ = [
|
||||
'monologue_agent',
|
||||
'codeact_agent',
|
||||
'planner_agent',
|
||||
'SWE_agent',
|
||||
'delegator_agent',
|
||||
'dummy_agent',
|
||||
]
|
||||
|
||||
for agent in all_microagents.values():
|
||||
name = agent['name']
|
||||
prompt = agent['prompt']
|
||||
|
||||
anon_class = type(name, (MicroAgent,), {
|
||||
'prompt': prompt,
|
||||
'agent_definition': agent,
|
||||
})
|
||||
anon_class = type(
|
||||
name,
|
||||
(MicroAgent,),
|
||||
{
|
||||
'prompt': prompt,
|
||||
'agent_definition': agent,
|
||||
},
|
||||
)
|
||||
|
||||
Agent.register(name, anon_class)
|
||||
|
||||
@@ -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."
|
||||
```
|
||||

|
||||
|
||||
Example: prompts `gpt-4-0125-preview` to write a flask server, install `flask` library, and start the server.
|
||||
## Plugin System
|
||||
|
||||
<img width="951" alt="image" src="https://github.com/OpenDevin/OpenDevin/assets/38853559/325c3115-a343-4cc5-a92b-f1e5d552a077">
|
||||
To make the CodeAct agent more powerful with only access to `bash` action space, CodeAct agent leverages OpenDevin's plugin system:
|
||||
- [Jupyter plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/jupyter): for IPython execution via bash command
|
||||
- [SWE-agent tool plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/swe_agent_commands): Powerful bash command line tools for software development tasks introduced by [swe-agent](https://github.com/princeton-nlp/swe-agent).
|
||||
|
||||
<img width="957" alt="image" src="https://github.com/OpenDevin/OpenDevin/assets/38853559/68ad10c1-744a-4e9d-bb29-0f163d665a0a">
|
||||
## Demo
|
||||
|
||||
Most of the things are working as expected, except at the end, the model did not follow the instruction to stop the interaction by outputting `<execute> exit </execute>` as instructed.
|
||||
https://github.com/OpenDevin/OpenDevin/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac
|
||||
|
||||
**TODO**: This should be fixable by either (1) including a complete in-context example like [this](https://github.com/xingyaoww/mint-bench/blob/main/mint/tasks/in_context_examples/reasoning/with_tool.txt), OR (2) collect some interaction data like this and fine-tune a model (like [this](https://github.com/xingyaoww/code-act), a more complex route).
|
||||
*Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)*
|
||||
|
||||
## Work-in-progress & Next step
|
||||
|
||||
[] Support web-browsing
|
||||
[] Complete the workflow for CodeAct agent to submit Github PRs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .codeact_agent import CodeActAgent
|
||||
|
||||
|
||||
@@ -1,64 +1,108 @@
|
||||
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.sandbox.plugins import JupyterRequirement, PluginRequirement
|
||||
from opendevin.state import State
|
||||
|
||||
SYSTEM_MESSAGE = """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>
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
### 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()]
|
||||
sandbox_plugins: List[PluginRequirement] = [
|
||||
JupyterRequirement(),
|
||||
SWEAgentCommandsRequirement(),
|
||||
]
|
||||
SUPPORTED_ACTIONS = (
|
||||
CmdRunAction,
|
||||
IPythonRunCellAction,
|
||||
AgentEchoAction,
|
||||
AgentTalkAction,
|
||||
NullAction,
|
||||
)
|
||||
SUPPORTED_OBSERVATIONS = (
|
||||
AgentMessageObservation,
|
||||
UserMessageObservation,
|
||||
CmdOutputObservation,
|
||||
IPythonRunCellObservation,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -75,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 ' 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')
|
||||
|
||||
226
agenthub/codeact_agent/prompt.py
Normal file
226
agenthub/codeact_agent/prompt.py
Normal 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'
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .agent import DelegatorAgent
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from typing import List
|
||||
|
||||
from opendevin.action import Action, AgentDelegateAction, AgentFinishAction
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
from opendevin.controller.state.state import State
|
||||
from opendevin.events.action import Action, AgentDelegateAction, AgentFinishAction
|
||||
from opendevin.events.observation import AgentDelegateObservation
|
||||
from opendevin.llm.llm import LLM
|
||||
from opendevin.observation import AgentDelegateObservation
|
||||
from opendevin.state import State
|
||||
|
||||
|
||||
class DelegatorAgent(Agent):
|
||||
@@ -12,6 +12,7 @@ class DelegatorAgent(Agent):
|
||||
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
|
||||
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
|
||||
"""
|
||||
|
||||
current_delegate: str = ''
|
||||
|
||||
def __init__(self, llm: LLM):
|
||||
@@ -37,9 +38,9 @@ class DelegatorAgent(Agent):
|
||||
"""
|
||||
if self.current_delegate == '':
|
||||
self.current_delegate = 'study'
|
||||
return AgentDelegateAction(agent='StudyRepoForTaskAgent', inputs={
|
||||
'task': state.plan.main_goal
|
||||
})
|
||||
return AgentDelegateAction(
|
||||
agent='StudyRepoForTaskAgent', inputs={'task': state.plan.main_goal}
|
||||
)
|
||||
|
||||
lastObservation = state.history[-1][1]
|
||||
if not isinstance(lastObservation, AgentDelegateObservation):
|
||||
@@ -47,24 +48,36 @@ class DelegatorAgent(Agent):
|
||||
|
||||
if self.current_delegate == 'study':
|
||||
self.current_delegate = 'coder'
|
||||
return AgentDelegateAction(agent='Coder', inputs={
|
||||
'task': state.plan.main_goal,
|
||||
'summary': lastObservation.outputs['summary'],
|
||||
})
|
||||
return AgentDelegateAction(
|
||||
agent='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,
|
||||
})
|
||||
return AgentDelegateAction(
|
||||
agent='Verifier',
|
||||
inputs={
|
||||
'task': state.plan.main_goal,
|
||||
},
|
||||
)
|
||||
elif self.current_delegate == 'verifier':
|
||||
if 'completed' in lastObservation.outputs and lastObservation.outputs['completed']:
|
||||
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'],
|
||||
})
|
||||
return AgentDelegateAction(
|
||||
agent='Coder',
|
||||
inputs={
|
||||
'task': state.plan.main_goal,
|
||||
'summary': lastObservation.outputs['summary'],
|
||||
},
|
||||
)
|
||||
else:
|
||||
raise Exception('Invalid delegate state')
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .agent import DummyAgent
|
||||
|
||||
Agent.register('DummyAgent', DummyAgent)
|
||||
|
||||
@@ -1,23 +1,159 @@
|
||||
"""Module for a Dummy agent."""
|
||||
import time
|
||||
from typing import List, TypedDict
|
||||
|
||||
from typing import List
|
||||
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
|
||||
|
||||
from opendevin.action import Action
|
||||
from opendevin.action.base import NullAction
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent_controller import AgentController
|
||||
from opendevin.observation.base import NullObservation, Observation
|
||||
from opendevin.state import State
|
||||
"""
|
||||
FIXME: There are a few problems this surfaced
|
||||
* FileWrites seem to add an unintended newline at the end of the file
|
||||
* command_id is sometimes a number, sometimes a string
|
||||
* Why isn't the output of the background command split between two steps?
|
||||
* Browser not working
|
||||
"""
|
||||
|
||||
ActionObs = TypedDict(
|
||||
'ActionObs', {'action': Action, 'observations': List[Observation]}
|
||||
)
|
||||
|
||||
BACKGROUND_CMD = 'echo "This is in the background" && sleep .1 && echo "This too"'
|
||||
|
||||
|
||||
class DummyAgent(Agent):
|
||||
"""A dummy agent that does nothing but can be used in testing."""
|
||||
"""
|
||||
The DummyAgent is used for e2e testing. It just sends the same set of actions deterministically,
|
||||
without making any LLM calls.
|
||||
"""
|
||||
|
||||
async def run(self, controller: AgentController) -> Observation:
|
||||
return NullObservation('')
|
||||
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:
|
||||
return NullAction('')
|
||||
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 []
|
||||
return ['I am a computer.']
|
||||
|
||||
14
agenthub/micro/README.md
Normal file
14
agenthub/micro/README.md
Normal 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.
|
||||
@@ -1,4 +0,0 @@
|
||||
* `add_task` - add a task to your plan. Arguments:
|
||||
* `parent` - the ID of the parent task
|
||||
* `goal` - the goal of the task
|
||||
* `subtasks` - a list of subtasks, each of which is a map with a `goal` key.
|
||||
@@ -1,2 +1,2 @@
|
||||
* `finish` - if ALL of your tasks and subtasks have been verified or abandoned, and you're absolutely certain that you've completed your task and have tested your work, use the finish action to stop working. Arguments:
|
||||
* `finish` - if you're absolutely certain that you've completed your task and have tested your work, use the finish action to stop working. Arguments:
|
||||
* `outputs` - a dictionary representing the outputs of your task, if any
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
* `modify_task` - close a task. Arguments:
|
||||
* `id` - the ID of the task to close
|
||||
* `state` - set to 'in_progress' to start the task, 'completed' to finish it, 'verified' to assert that it was successful, 'abandoned' to give up on it permanently, or `open` to stop working on it for now.
|
||||
@@ -1,13 +1,14 @@
|
||||
import copy
|
||||
import json
|
||||
from typing import Dict, List
|
||||
|
||||
from jinja2 import BaseLoader, Environment
|
||||
|
||||
from opendevin.action import Action, action_from_dict
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.exceptions import LLMOutputError
|
||||
from opendevin.controller.agent import Agent
|
||||
from opendevin.controller.state.state import State
|
||||
from opendevin.core.exceptions import LLMOutputError
|
||||
from opendevin.events.action import Action, action_from_dict
|
||||
from opendevin.llm.llm import LLM
|
||||
from opendevin.state import State
|
||||
|
||||
from .instructions import instructions
|
||||
from .registry import all_microagents
|
||||
@@ -41,11 +42,39 @@ def my_encoder(obj):
|
||||
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
|
||||
"""
|
||||
return json.dumps(obj, default=my_encoder, **kwargs)
|
||||
# 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):
|
||||
@@ -65,7 +94,8 @@ class MicroAgent(Agent):
|
||||
state=state,
|
||||
instructions=instructions,
|
||||
to_json=to_json,
|
||||
delegates=self.delegates)
|
||||
delegates=self.delegates,
|
||||
)
|
||||
messages = [{'content': prompt, 'role': 'user'}]
|
||||
resp = self.llm.completion(messages=messages)
|
||||
action_resp = resp['choices'][0]['message']['content']
|
||||
|
||||
@@ -4,9 +4,11 @@ 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 }}
|
||||
|
||||
25
agenthub/micro/commit_writer/README.md
Normal file
25
agenthub/micro/commit_writer/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
## Introduction
|
||||
|
||||
CommitWriterAgent can help write git commit message. Example:
|
||||
|
||||
```bash
|
||||
WORKSPACE_MOUNT_PATH="`PWD`" SANDBOX_TYPE="exec" \
|
||||
poetry run python opendevin/core/main.py -t "dummy task" -c CommitWriterAgent -d ./
|
||||
```
|
||||
|
||||
This agent is special in the sense that it doesn't need a task. Once called,
|
||||
it attempts to read all diff in the git staging area and write a good commit
|
||||
message.
|
||||
|
||||
## Future work
|
||||
|
||||
### Feedback loop
|
||||
|
||||
The commit message could be (optionally) shown to the customer or
|
||||
other agents, so that CommitWriterAgent could gather feedback to further
|
||||
improve the commit message.
|
||||
|
||||
### Task rejection
|
||||
|
||||
When the agent cannot compile a commit message (e.g. not git repository), it
|
||||
should reject the task with an explanation.
|
||||
6
agenthub/micro/commit_writer/agent.yaml
Normal file
6
agenthub/micro/commit_writer/agent.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
name: CommitWriterAgent
|
||||
description: "Write a git commit message for files in the git staging area"
|
||||
generates: Action
|
||||
inputs: {}
|
||||
outputs:
|
||||
answer: string
|
||||
28
agenthub/micro/commit_writer/prompt.md
Normal file
28
agenthub/micro/commit_writer/prompt.md
Normal 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 }}
|
||||
@@ -1,5 +1,5 @@
|
||||
# Task
|
||||
You are a brilliant mathematician and programmer. You've been given the follwoing problem to solve:
|
||||
You are a brilliant mathematician and programmer. You've been given the following problem to solve:
|
||||
|
||||
{{ state.plan.main_goal }}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ the information needed to complete this task:
|
||||
You must ONLY `run` commands that have no side-effects, like `ls` and `grep`.
|
||||
|
||||
Do NOT finish until you have a complete understanding of which parts of the
|
||||
codebase are relevant to the task, including particular files, function, functions, and classes.
|
||||
codebase are relevant to the task, including particular files, functions, and classes.
|
||||
When you're done, put your summary in `outputs.summary` in the `finish` action.
|
||||
|
||||
## History
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .agent import MonologueAgent
|
||||
|
||||
|
||||
@@ -2,8 +2,13 @@ from typing import List
|
||||
|
||||
import agenthub.monologue_agent.utils.prompts as prompts
|
||||
from agenthub.monologue_agent.utils.monologue import Monologue
|
||||
from opendevin import config
|
||||
from opendevin.action import (
|
||||
from opendevin.controller.agent import Agent
|
||||
from opendevin.controller.state.state import State
|
||||
from opendevin.core import config
|
||||
from opendevin.core.exceptions import AgentNoInstructionError
|
||||
from opendevin.core.schema import ActionType
|
||||
from opendevin.core.schema.config import ConfigType
|
||||
from opendevin.events.action import (
|
||||
Action,
|
||||
AgentRecallAction,
|
||||
AgentThinkAction,
|
||||
@@ -14,10 +19,7 @@ from opendevin.action import (
|
||||
GitHubPushAction,
|
||||
NullAction,
|
||||
)
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.exceptions import AgentNoInstructionError
|
||||
from opendevin.llm.llm import LLM
|
||||
from opendevin.observation import (
|
||||
from opendevin.events.observation import (
|
||||
AgentRecallObservation,
|
||||
BrowserOutputObservation,
|
||||
CmdOutputObservation,
|
||||
@@ -25,9 +27,7 @@ from opendevin.observation import (
|
||||
NullObservation,
|
||||
Observation,
|
||||
)
|
||||
from opendevin.schema import ActionType
|
||||
from opendevin.schema.config import ConfigType
|
||||
from opendevin.state import State
|
||||
from opendevin.llm.llm import LLM
|
||||
|
||||
if config.get(ConfigType.AGENT_MEMORY_ENABLED):
|
||||
from agenthub.monologue_agent.utils.memory import LongTermMemory
|
||||
@@ -56,7 +56,7 @@ INITIAL_THOUGHTS = [
|
||||
'RUN echo "hello world"',
|
||||
'hello world',
|
||||
'Cool! I bet I can write files too using the write action.',
|
||||
"WRITE echo \"console.log('hello world')\" > test.js",
|
||||
'WRITE echo "console.log(\'hello world\')" > test.js',
|
||||
'',
|
||||
"I just created test.js. I'll try and run it now.",
|
||||
'RUN node test.js',
|
||||
@@ -133,9 +133,10 @@ class MonologueAgent(Agent):
|
||||
|
||||
def _initialize(self, task: str):
|
||||
"""
|
||||
Utilizes the INITIAL_THOUGHTS list to give the agent a context for it's capabilities
|
||||
Utilizes the INITIAL_THOUGHTS list to give the agent a context for its capabilities
|
||||
and how to navigate the WORKSPACE_MOUNT_PATH_IN_SANDBOX in `config` (e.g., /workspace by default).
|
||||
Short circuited to return when already initialized.
|
||||
Will execute again when called after reset.
|
||||
|
||||
Parameters:
|
||||
- task (str): The initial goal statement provided by the user
|
||||
@@ -156,6 +157,10 @@ class MonologueAgent(Agent):
|
||||
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)
|
||||
@@ -168,8 +173,7 @@ class MonologueAgent(Agent):
|
||||
elif previous_action == ActionType.READ:
|
||||
observation = FileReadObservation(content=thought, path='')
|
||||
elif previous_action == ActionType.RECALL:
|
||||
observation = AgentRecallObservation(
|
||||
content=thought, memories=[])
|
||||
observation = AgentRecallObservation(content=thought, memories=[])
|
||||
elif previous_action == ActionType.BROWSE:
|
||||
observation = BrowserOutputObservation(
|
||||
content=thought, url='', screenshot=''
|
||||
@@ -207,7 +211,6 @@ class MonologueAgent(Agent):
|
||||
else:
|
||||
action = AgentThinkAction(thought=thought)
|
||||
self._add_event(action.to_memory())
|
||||
self._initialized = True
|
||||
|
||||
def step(self, state: State) -> Action:
|
||||
"""
|
||||
@@ -256,8 +259,6 @@ class MonologueAgent(Agent):
|
||||
|
||||
def reset(self) -> None:
|
||||
super().reset()
|
||||
self.monologue = Monologue()
|
||||
if config.get(ConfigType.AGENT_MEMORY_ENABLED):
|
||||
self.memory = LongTermMemory()
|
||||
else:
|
||||
self.memory = None
|
||||
|
||||
# Reset the initial monologue and memory
|
||||
self._initialized = False
|
||||
|
||||
@@ -13,9 +13,9 @@ from tenacity import (
|
||||
wait_random_exponential,
|
||||
)
|
||||
|
||||
from opendevin import config
|
||||
from opendevin.logger import opendevin_logger as logger
|
||||
from opendevin.schema.config import ConfigType
|
||||
from opendevin.core import config
|
||||
from opendevin.core.logger import opendevin_logger as logger
|
||||
from opendevin.core.schema.config import ConfigType
|
||||
|
||||
from . import json
|
||||
|
||||
@@ -37,15 +37,22 @@ else:
|
||||
|
||||
|
||||
def attempt_on_error(retry_state):
|
||||
logger.error(f'{retry_state.outcome.exception()}. Attempt #{retry_state.attempt_number} | You can customize these settings in the configuration.', exc_info=False)
|
||||
logger.error(
|
||||
f'{retry_state.outcome.exception()}. Attempt #{retry_state.attempt_number} | You can customize these settings in the configuration.',
|
||||
exc_info=False,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@retry(reraise=True,
|
||||
stop=stop_after_attempt(num_retries),
|
||||
wait=wait_random_exponential(min=retry_min_wait, max=retry_max_wait),
|
||||
retry=retry_if_exception_type((RateLimitError, APIConnectionError, InternalServerError)),
|
||||
after=attempt_on_error)
|
||||
@retry(
|
||||
reraise=True,
|
||||
stop=stop_after_attempt(num_retries),
|
||||
wait=wait_random_exponential(min=retry_min_wait, max=retry_max_wait),
|
||||
retry=retry_if_exception_type(
|
||||
(RateLimitError, APIConnectionError, InternalServerError)
|
||||
),
|
||||
after=attempt_on_error,
|
||||
)
|
||||
def wrapper_get_embeddings(*args, **kwargs):
|
||||
return original_get_embeddings(*args, **kwargs)
|
||||
|
||||
@@ -56,9 +63,16 @@ embedding_strategy = config.get(ConfigType.LLM_EMBEDDING_MODEL)
|
||||
|
||||
# TODO: More embeddings: https://docs.llamaindex.ai/en/stable/examples/embeddings/OpenAI/
|
||||
# There's probably a more programmatic way to do this.
|
||||
supported_ollama_embed_models = ['llama2', 'mxbai-embed-large', 'nomic-embed-text', 'all-minilm', 'stable-code']
|
||||
supported_ollama_embed_models = [
|
||||
'llama2',
|
||||
'mxbai-embed-large',
|
||||
'nomic-embed-text',
|
||||
'all-minilm',
|
||||
'stable-code',
|
||||
]
|
||||
if embedding_strategy in supported_ollama_embed_models:
|
||||
from llama_index.embeddings.ollama import OllamaEmbedding
|
||||
|
||||
embed_model = OllamaEmbedding(
|
||||
model_name=embedding_strategy,
|
||||
base_url=config.get(ConfigType.LLM_EMBEDDING_BASE_URL, required=True),
|
||||
@@ -66,16 +80,20 @@ if embedding_strategy in supported_ollama_embed_models:
|
||||
)
|
||||
elif embedding_strategy == 'openai':
|
||||
from llama_index.embeddings.openai import OpenAIEmbedding
|
||||
|
||||
embed_model = OpenAIEmbedding(
|
||||
model='text-embedding-ada-002',
|
||||
api_key=config.get(ConfigType.LLM_API_KEY, required=True)
|
||||
api_key=config.get(ConfigType.LLM_API_KEY, required=True),
|
||||
)
|
||||
elif embedding_strategy == 'azureopenai':
|
||||
# Need to instruct to set these env variables in documentation
|
||||
from llama_index.embeddings.azure_openai import AzureOpenAIEmbedding
|
||||
|
||||
embed_model = AzureOpenAIEmbedding(
|
||||
model='text-embedding-ada-002',
|
||||
deployment_name=config.get(ConfigType.LLM_EMBEDDING_DEPLOYMENT_NAME, required=True),
|
||||
deployment_name=config.get(
|
||||
ConfigType.LLM_EMBEDDING_DEPLOYMENT_NAME, required=True
|
||||
),
|
||||
api_key=config.get(ConfigType.LLM_API_KEY, required=True),
|
||||
azure_endpoint=config.get(ConfigType.LLM_BASE_URL, required=True),
|
||||
api_version=config.get(ConfigType.LLM_API_VERSION, required=True),
|
||||
@@ -87,9 +105,8 @@ elif (embedding_strategy is not None) and (embedding_strategy.lower() == 'none')
|
||||
embed_model = None
|
||||
else:
|
||||
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
||||
embed_model = HuggingFaceEmbedding(
|
||||
model_name='BAAI/bge-small-en-v1.5'
|
||||
)
|
||||
|
||||
embed_model = HuggingFaceEmbedding(model_name='BAAI/bge-small-en-v1.5')
|
||||
|
||||
|
||||
sema = threading.Semaphore(value=config.get(ConfigType.AGENT_MEMORY_MAX_THREADS))
|
||||
@@ -109,7 +126,8 @@ class LongTermMemory:
|
||||
self.collection = db.get_or_create_collection(name='memories')
|
||||
vector_store = ChromaVectorStore(chroma_collection=self.collection)
|
||||
self.index = VectorStoreIndex.from_vector_store(
|
||||
vector_store, embed_model=embed_model)
|
||||
vector_store, embed_model=embed_model
|
||||
)
|
||||
self.thought_idx = 0
|
||||
self._add_threads = []
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
import agenthub.monologue_agent.utils.json as json
|
||||
import agenthub.monologue_agent.utils.prompts as prompts
|
||||
from opendevin.exceptions import AgentEventTypeError
|
||||
from opendevin.core.exceptions import AgentEventTypeError
|
||||
from opendevin.core.logger import opendevin_logger as logger
|
||||
from opendevin.llm.llm import LLM
|
||||
from opendevin.logger import opendevin_logger as logger
|
||||
|
||||
|
||||
class Monologue:
|
||||
|
||||
@@ -2,16 +2,16 @@ import re
|
||||
from json import JSONDecodeError
|
||||
from typing import List
|
||||
|
||||
from opendevin import config
|
||||
from opendevin.action import (
|
||||
from opendevin.core import config
|
||||
from opendevin.core.exceptions import LLMOutputError
|
||||
from opendevin.core.schema.config import ConfigType
|
||||
from opendevin.events.action import (
|
||||
Action,
|
||||
action_from_dict,
|
||||
)
|
||||
from opendevin.exceptions import LLMOutputError
|
||||
from opendevin.observation import (
|
||||
from opendevin.events.observation import (
|
||||
CmdOutputObservation,
|
||||
)
|
||||
from opendevin.schema.config import ConfigType
|
||||
|
||||
from . import json
|
||||
|
||||
@@ -158,7 +158,9 @@ def get_request_action_prompt(
|
||||
'hint': hint,
|
||||
'user': user,
|
||||
'timeout': config.get(ConfigType.SANDBOX_TIMEOUT),
|
||||
'WORKSPACE_MOUNT_PATH_IN_SANDBOX': config.get(ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX),
|
||||
'WORKSPACE_MOUNT_PATH_IN_SANDBOX': config.get(
|
||||
ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -178,12 +180,18 @@ def parse_action_response(response: str) -> Action:
|
||||
# Find response-looking json in the output and use the more promising one. Helps with weak llms
|
||||
response_json_matches = re.finditer(
|
||||
r"""{\s*\"action\":\s?\"(\w+)\"(?:,?|,\s*\"args\":\s?{((?:.|\s)*?)})\s*}""",
|
||||
response) # Find all response-looking strings
|
||||
response,
|
||||
) # Find all response-looking strings
|
||||
|
||||
def rank(match):
|
||||
return len(match[2]) if match[1] == 'think' else 130 # Crudely rank multiple responses by length
|
||||
return (
|
||||
len(match[2]) if match[1] == 'think' else 130
|
||||
) # Crudely rank multiple responses by length
|
||||
|
||||
try:
|
||||
action_dict = json.loads(max(response_json_matches, key=rank)[0]) # Use the highest ranked response
|
||||
action_dict = json.loads(
|
||||
max(response_json_matches, key=rank)[0]
|
||||
) # Use the highest ranked response
|
||||
except (ValueError, JSONDecodeError):
|
||||
raise LLMOutputError(
|
||||
'Invalid JSON, the response must be well-formed JSON as specified in the prompt.'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
|
||||
from .agent import PlannerAgent
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import List
|
||||
|
||||
from opendevin.action import Action, AgentFinishAction
|
||||
from opendevin.agent import Agent
|
||||
from opendevin.controller.agent import Agent
|
||||
from opendevin.controller.state.state import State
|
||||
from opendevin.events.action import Action, AgentFinishAction
|
||||
from opendevin.llm.llm import LLM
|
||||
from opendevin.state import State
|
||||
|
||||
from .prompt import get_prompt, parse_response
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import json
|
||||
from typing import Dict, List, Tuple, Type
|
||||
|
||||
from opendevin.action import (
|
||||
from opendevin.controller.state.plan import Plan
|
||||
from opendevin.core.logger import opendevin_logger as logger
|
||||
from opendevin.core.schema import ActionType
|
||||
from opendevin.events.action import (
|
||||
Action,
|
||||
AddTaskAction,
|
||||
AgentFinishAction,
|
||||
@@ -17,13 +20,10 @@ from opendevin.action import (
|
||||
NullAction,
|
||||
action_from_dict,
|
||||
)
|
||||
from opendevin.logger import opendevin_logger as logger
|
||||
from opendevin.observation import (
|
||||
from opendevin.events.observation import (
|
||||
NullObservation,
|
||||
Observation,
|
||||
)
|
||||
from opendevin.plan import Plan
|
||||
from opendevin.schema import ActionType
|
||||
|
||||
ACTION_TYPE_TO_CLASS: Dict[str, Type[Action]] = {
|
||||
ActionType.RUN: CmdRunAction,
|
||||
@@ -131,7 +131,7 @@ What is your next thought or action? Again, you must reply with JSON, and only w
|
||||
|
||||
|
||||
def get_hint(latest_action_id: str) -> str:
|
||||
""" Returns action type hint based on given action_id """
|
||||
"""Returns action type hint based on given action_id"""
|
||||
|
||||
hints = {
|
||||
'': "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with.",
|
||||
|
||||
@@ -5,7 +5,7 @@ WORKDIR /app
|
||||
|
||||
COPY ./frontend/package.json frontend/package-lock.json ./
|
||||
RUN npm install -g npm@10.5.1
|
||||
RUN npm install
|
||||
RUN npm ci
|
||||
|
||||
COPY ./frontend ./
|
||||
RUN npm run make-i18n && npm run build
|
||||
@@ -32,7 +32,8 @@ 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
|
||||
@@ -40,19 +41,35 @@ 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 ./opendevin ./opendevin
|
||||
COPY ./agenthub ./agenthub
|
||||
RUN python opendevin/download.py # No-op to download assets
|
||||
COPY --chown=opendevin:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||
RUN playwright install --with-deps chromium
|
||||
|
||||
COPY --from=frontend-builder /app/dist ./frontend/dist
|
||||
COPY --chown=opendevin:app --chmod=770 ./opendevin ./opendevin
|
||||
COPY --chown=opendevin:app --chmod=777 ./opendevin/runtime/plugins ./opendevin/runtime/plugins
|
||||
COPY --chown=opendevin:app --chmod=770 ./agenthub ./agenthub
|
||||
|
||||
CMD ["uvicorn", "opendevin.server.listen:app", "--host", "0.0.0.0", "--port", "3000"]
|
||||
RUN python opendevin/core/download.py # No-op to download assets
|
||||
RUN chown -R opendevin:app /app/logs && chmod -R 770 /app/logs # This gets created by the download.py script
|
||||
|
||||
|
||||
COPY --chown=opendevin:app --chmod=770 --from=frontend-builder /app/dist ./frontend/dist
|
||||
COPY --chown=opendevin:app --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
|
||||
|
||||
USER root
|
||||
CMD ["/app/entrypoint.sh"]
|
||||
|
||||
26
containers/app/entrypoint.sh
Executable file
26
containers/app/entrypoint.sh
Executable 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"
|
||||
@@ -18,7 +18,7 @@ cache_tag="$cache_tag_base"
|
||||
|
||||
if [[ -n $GITHUB_REF_NAME ]]; then
|
||||
# check if ref name is a version number
|
||||
if [[ $GITHUB_REF_NAME =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
if [[ $GITHUB_REF_NAME =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
major_version=$(echo $GITHUB_REF_NAME | cut -d. -f1)
|
||||
minor_version=$(echo $GITHUB_REF_NAME | cut -d. -f1,2)
|
||||
tags+=($major_version $minor_version)
|
||||
|
||||
@@ -27,3 +27,7 @@ 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
|
||||
|
||||
@@ -20,16 +20,16 @@ repos:
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.7
|
||||
rev: v0.4.1
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
entry: ruff check --config dev_config/python/ruff.toml opendevin/ agenthub/
|
||||
entry: ruff check --config dev_config/python/ruff.toml
|
||||
types_or: [python, pyi, jupyter]
|
||||
args: [--fix]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
entry: ruff check --config dev_config/python/ruff.toml opendevin/ agenthub/
|
||||
entry: ruff format --config dev_config/python/ruff.toml
|
||||
types_or: [python, pyi, jupyter]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
@@ -41,8 +41,3 @@ repos:
|
||||
entry: mypy --config-file dev_config/python/mypy.ini opendevin/ agenthub/
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
|
||||
- repo: https://github.com/NiklasRosenstein/pydoc-markdown
|
||||
rev: develop
|
||||
hooks:
|
||||
- id: pydoc-markdown
|
||||
|
||||
@@ -15,7 +15,9 @@ ignore = [
|
||||
"E501",
|
||||
]
|
||||
|
||||
flake8-quotes = {inline-quotes = "single"}
|
||||
[lint.flake8-quotes]
|
||||
docstring-quotes = "double"
|
||||
inline-quotes = "single"
|
||||
|
||||
[format]
|
||||
quote-style = "single"
|
||||
|
||||
@@ -98,11 +98,11 @@ const config: Config = {
|
||||
items: [
|
||||
{
|
||||
label: "Slack",
|
||||
href: "https://join.slack.com/t/opendevin/shared_invite/zt-2etftj1dd-X1fDL2PYIVpsmJZkqEYANw",
|
||||
href: "https://join.slack.com/t/opendevin/shared_invite/zt-2ggtwn3k5-PvAA2LUmqGHVZ~XzGq~ILw"
|
||||
},
|
||||
{
|
||||
label: "Discord",
|
||||
href: "https://discord.gg/mBuDGRzzES",
|
||||
href: "https://discord.gg/ESHStjSjD4",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: agenthub.SWE_agent.agent
|
||||
---
|
||||
|
||||
## SWEAgent Objects
|
||||
|
||||
```python
|
||||
class SWEAgent(Agent)
|
||||
```
|
||||
|
||||
An attempt to recreate swe_agent with output parsing, prompting style, and Application Computer Interface (ACI).
|
||||
|
||||
SWE-agent includes ACI functions like 'goto', 'search_for', 'edit', 'scroll', 'run'
|
||||
|
||||
#### step
|
||||
|
||||
```python
|
||||
def step(state: State) -> Action
|
||||
```
|
||||
|
||||
SWE-Agent step:
|
||||
1. Get context - past actions, custom commands, current step
|
||||
2. Perform think-act - prompt model for action and reasoning
|
||||
3. Catch errors - ensure model takes action (5 attempts max)
|
||||
|
||||
#### reset
|
||||
|
||||
```python
|
||||
def reset() -> None
|
||||
```
|
||||
|
||||
Used to reset the agent
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
sidebar_label: parser
|
||||
title: agenthub.SWE_agent.parser
|
||||
---
|
||||
|
||||
#### get\_action\_from\_string
|
||||
|
||||
```python
|
||||
def get_action_from_string(command_string: str,
|
||||
path: str,
|
||||
line: int,
|
||||
thoughts: str = '') -> Action | None
|
||||
```
|
||||
|
||||
Parses the command string to find which command the agent wants to run
|
||||
Converts the command into a proper Action and returns
|
||||
|
||||
#### parse\_command
|
||||
|
||||
```python
|
||||
def parse_command(input_str: str, path: str, line: int)
|
||||
```
|
||||
|
||||
Parses a given string and separates the command (enclosed in triple backticks) from any accompanying text.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `input_str` _str_ - The input string to be parsed.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- `tuple` - A tuple containing the command and the accompanying text (if any).
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
sidebar_label: codeact_agent
|
||||
title: agenthub.codeact_agent.codeact_agent
|
||||
---
|
||||
|
||||
## CodeActAgent Objects
|
||||
|
||||
```python
|
||||
class CodeActAgent(Agent)
|
||||
```
|
||||
|
||||
The Code Act Agent is a minimalist agent.
|
||||
The agent works by passing the model a list of action-observation pairs and prompting the model to take the next step.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(llm: LLM) -> None
|
||||
```
|
||||
|
||||
Initializes a new instance of the CodeActAgent class.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
|
||||
#### step
|
||||
|
||||
```python
|
||||
def step(state: State) -> Action
|
||||
```
|
||||
|
||||
Performs one step using the Code Act Agent.
|
||||
This includes gathering info on previous steps and prompting the model to make a command to execute.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- state (State): used to get updated info and background commands
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- CmdRunAction(command) - command action to run
|
||||
- AgentEchoAction(content=INVALID_INPUT_MESSAGE) - invalid command output
|
||||
|
||||
|
||||
**Raises**:
|
||||
|
||||
- NotImplementedError - for actions other than CmdOutputObservation or AgentMessageObservation
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: agenthub.delegator_agent.agent
|
||||
---
|
||||
|
||||
## DelegatorAgent Objects
|
||||
|
||||
```python
|
||||
class DelegatorAgent(Agent)
|
||||
```
|
||||
|
||||
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
|
||||
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(llm: LLM)
|
||||
```
|
||||
|
||||
Initialize the Delegator Agent with an LLM
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
|
||||
#### step
|
||||
|
||||
```python
|
||||
def step(state: State) -> Action
|
||||
```
|
||||
|
||||
Checks to see if current step is completed, returns AgentFinishAction if True.
|
||||
Otherwise, creates a plan prompt and sends to model for inference, returning the result as the next action.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- state (State): The current state given the previous actions and observations
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- AgentFinishAction: If the last state was 'completed', 'verified', or 'abandoned'
|
||||
- Action: The next action to take based on llm response
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: agenthub.dummy_agent.agent
|
||||
---
|
||||
|
||||
Module for a Dummy agent.
|
||||
|
||||
## DummyAgent Objects
|
||||
|
||||
```python
|
||||
class DummyAgent(Agent)
|
||||
```
|
||||
|
||||
A dummy agent that does nothing but can be used in testing.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: agenthub.micro.agent
|
||||
---
|
||||
|
||||
#### my\_encoder
|
||||
|
||||
```python
|
||||
def my_encoder(obj)
|
||||
```
|
||||
|
||||
Encodes objects as dictionaries
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- obj (Object): An object that will be converted
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- dict: If the object can be converted it is returned in dict format
|
||||
|
||||
#### to\_json
|
||||
|
||||
```python
|
||||
def to_json(obj, **kwargs)
|
||||
```
|
||||
|
||||
Serialize an object to str format
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: agenthub.monologue_agent.agent
|
||||
---
|
||||
|
||||
## MonologueAgent Objects
|
||||
|
||||
```python
|
||||
class MonologueAgent(Agent)
|
||||
```
|
||||
|
||||
The Monologue Agent utilizes long and short term memory to complete tasks.
|
||||
Long term memory is stored as a LongTermMemory object and the model uses it to search for examples from the past.
|
||||
Short term memory is stored as a Monologue object and the model can condense it as necessary.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(llm: LLM)
|
||||
```
|
||||
|
||||
Initializes the Monologue Agent with an llm, monologue, and memory.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
|
||||
#### step
|
||||
|
||||
```python
|
||||
def step(state: State) -> Action
|
||||
```
|
||||
|
||||
Modifies the current state by adding the most recent actions and observations, then prompts the model to think about it's next action to take using monologue, memory, and hint.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- state (State): The current state based on previous steps taken
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- Action: The next action to take based on LLM response
|
||||
|
||||
#### search\_memory
|
||||
|
||||
```python
|
||||
def search_memory(query: str) -> List[str]
|
||||
```
|
||||
|
||||
Uses VectorIndexRetriever to find related memories within the long term memory.
|
||||
Uses search to produce top 10 results.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- query (str): The query that we want to find related memories for
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- List[str]: A list of top 10 text results that matched the query
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
sidebar_label: json
|
||||
title: agenthub.monologue_agent.utils.json
|
||||
---
|
||||
|
||||
#### my\_encoder
|
||||
|
||||
```python
|
||||
def my_encoder(obj)
|
||||
```
|
||||
|
||||
Encodes objects as dictionaries
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- obj (Object): An object that will be converted
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- dict: If the object can be converted it is returned in dict format
|
||||
|
||||
#### dumps
|
||||
|
||||
```python
|
||||
def dumps(obj, **kwargs)
|
||||
```
|
||||
|
||||
Serialize an object to str format
|
||||
|
||||
#### loads
|
||||
|
||||
```python
|
||||
def loads(s, **kwargs)
|
||||
```
|
||||
|
||||
Create a JSON object from str
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
sidebar_label: memory
|
||||
title: agenthub.monologue_agent.utils.memory
|
||||
---
|
||||
|
||||
## LongTermMemory Objects
|
||||
|
||||
```python
|
||||
class LongTermMemory()
|
||||
```
|
||||
|
||||
Responsible for storing information that the agent can call on later for better insights and context.
|
||||
Uses chromadb to store and search through memories.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__()
|
||||
```
|
||||
|
||||
Initialize the chromadb and set up ChromaVectorStore for later use.
|
||||
|
||||
#### add\_event
|
||||
|
||||
```python
|
||||
def add_event(event: dict)
|
||||
```
|
||||
|
||||
Adds a new event to the long term memory with a unique id.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- event (dict): The new event to be added to memory
|
||||
|
||||
#### search
|
||||
|
||||
```python
|
||||
def search(query: str, k: int = 10)
|
||||
```
|
||||
|
||||
Searches through the current memory using VectorIndexRetriever
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- query (str): A query to match search results to
|
||||
- k (int): Number of top results to return
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- List[str]: List of top k results found in current memory
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
sidebar_label: monologue
|
||||
title: agenthub.monologue_agent.utils.monologue
|
||||
---
|
||||
|
||||
## Monologue Objects
|
||||
|
||||
```python
|
||||
class Monologue()
|
||||
```
|
||||
|
||||
The monologue is a representation for the agent's internal monologue where it can think.
|
||||
The agent has the capability of using this monologue for whatever it wants.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__()
|
||||
```
|
||||
|
||||
Initialize the empty list of thoughts
|
||||
|
||||
#### add\_event
|
||||
|
||||
```python
|
||||
def add_event(t: dict)
|
||||
```
|
||||
|
||||
Adds an event to memory if it is a valid event.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- t (dict): The thought that we want to add to memory
|
||||
|
||||
|
||||
**Raises**:
|
||||
|
||||
- AgentEventTypeError: If t is not a dict
|
||||
|
||||
#### get\_thoughts
|
||||
|
||||
```python
|
||||
def get_thoughts()
|
||||
```
|
||||
|
||||
Get the current thoughts of the agent.
|
||||
|
||||
**Returns**:
|
||||
|
||||
- List: The list of thoughts that the agent has.
|
||||
|
||||
#### get\_total\_length
|
||||
|
||||
```python
|
||||
def get_total_length()
|
||||
```
|
||||
|
||||
Gives the total number of characters in all thoughts
|
||||
|
||||
**Returns**:
|
||||
|
||||
- Int: Total number of chars in thoughts.
|
||||
|
||||
#### condense
|
||||
|
||||
```python
|
||||
def condense(llm: LLM)
|
||||
```
|
||||
|
||||
Attempts to condense the monologue by using the llm
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- llm (LLM): llm to be used for summarization
|
||||
|
||||
|
||||
**Raises**:
|
||||
|
||||
- Exception: the same exception as it got from the llm or processing the response
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
sidebar_label: prompts
|
||||
title: agenthub.monologue_agent.utils.prompts
|
||||
---
|
||||
|
||||
#### get\_summarize\_monologue\_prompt
|
||||
|
||||
```python
|
||||
def get_summarize_monologue_prompt(thoughts: List[dict])
|
||||
```
|
||||
|
||||
Gets the prompt for summarizing the monologue
|
||||
|
||||
**Returns**:
|
||||
|
||||
- str: A formatted string with the current monologue within the prompt
|
||||
|
||||
#### get\_request\_action\_prompt
|
||||
|
||||
```python
|
||||
def get_request_action_prompt(
|
||||
task: str,
|
||||
thoughts: List[dict],
|
||||
background_commands_obs: List[CmdOutputObservation] = [])
|
||||
```
|
||||
|
||||
Gets the action prompt formatted with appropriate values.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- task (str): The current task the agent is trying to accomplish
|
||||
- thoughts (List[dict]): The agent's current thoughts
|
||||
- background_commands_obs (List[CmdOutputObservation]): List of all observed background commands running
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- str: Formatted prompt string with hint, task, monologue, and background included
|
||||
|
||||
#### parse\_action\_response
|
||||
|
||||
```python
|
||||
def parse_action_response(response: str) -> Action
|
||||
```
|
||||
|
||||
Parses a string to find an action within it
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- response (str): The string to be parsed
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- Action: The action that was found in the response string
|
||||
|
||||
#### parse\_summary\_response
|
||||
|
||||
```python
|
||||
def parse_summary_response(response: str) -> List[dict]
|
||||
```
|
||||
|
||||
Parses a summary of the monologue
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- response (str): The response string to be parsed
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- List[dict]: The list of summaries output by the model
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: agenthub.planner_agent.agent
|
||||
---
|
||||
|
||||
## PlannerAgent Objects
|
||||
|
||||
```python
|
||||
class PlannerAgent(Agent)
|
||||
```
|
||||
|
||||
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
|
||||
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(llm: LLM)
|
||||
```
|
||||
|
||||
Initialize the Planner Agent with an LLM
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
|
||||
#### step
|
||||
|
||||
```python
|
||||
def step(state: State) -> Action
|
||||
```
|
||||
|
||||
Checks to see if current step is completed, returns AgentFinishAction if True.
|
||||
Otherwise, creates a plan prompt and sends to model for inference, returning the result as the next action.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- state (State): The current state given the previous actions and observations
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- AgentFinishAction: If the last state was 'completed', 'verified', or 'abandoned'
|
||||
- Action: The next action to take based on llm response
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
sidebar_label: prompt
|
||||
title: agenthub.planner_agent.prompt
|
||||
---
|
||||
|
||||
#### get\_hint
|
||||
|
||||
```python
|
||||
def get_hint(latest_action_id: str) -> str
|
||||
```
|
||||
|
||||
Returns action type hint based on given action_id
|
||||
|
||||
#### get\_prompt
|
||||
|
||||
```python
|
||||
def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str
|
||||
```
|
||||
|
||||
Gets the prompt for the planner agent.
|
||||
Formatted with the most recent action-observation pairs, current task, and hint based on last action
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- plan (Plan): The original plan outlined by the user with LLM defined tasks
|
||||
- history (List[Tuple[Action, Observation]]): List of corresponding action-observation pairs
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- str: The formatted string prompt with historical values
|
||||
|
||||
#### parse\_response
|
||||
|
||||
```python
|
||||
def parse_response(response: str) -> Action
|
||||
```
|
||||
|
||||
Parses the model output to find a valid action to take
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- response (str): A response from the model that potentially contains an Action.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- Action: A valid next action to perform from model output
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
sidebar_label: action
|
||||
title: opendevin.action
|
||||
---
|
||||
|
||||
#### ACTION\_TYPE\_TO\_CLASS
|
||||
|
||||
type: ignore[attr-defined]
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: base
|
||||
title: opendevin.action.base
|
||||
---
|
||||
|
||||
## NullAction Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class NullAction(NotExecutableAction)
|
||||
```
|
||||
|
||||
An action that does nothing.
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
sidebar_label: fileop
|
||||
title: opendevin.action.fileop
|
||||
---
|
||||
|
||||
## FileReadAction Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FileReadAction(ExecutableAction)
|
||||
```
|
||||
|
||||
Reads a file from a given path.
|
||||
Can be set to read specific lines using start and end
|
||||
Default lines 0:-1 (whole file)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
sidebar_label: github
|
||||
title: opendevin.action.github
|
||||
---
|
||||
|
||||
## GitHubPushAction Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GitHubPushAction(ExecutableAction)
|
||||
```
|
||||
|
||||
This pushes the current branch to github.
|
||||
|
||||
To use this, you need to set the GITHUB_TOKEN environment variable.
|
||||
The agent will return a message with a URL that you can click to make a pull
|
||||
request.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `owner` - The owner of the source repo
|
||||
- `repo` - The name of the source repo
|
||||
- `branch` - The branch to push
|
||||
- `action` - The action identifier
|
||||
|
||||
## GitHubSendPRAction Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GitHubSendPRAction(ExecutableAction)
|
||||
```
|
||||
|
||||
An action to send a github PR.
|
||||
|
||||
To use this, you need to set the GITHUB_TOKEN environment variable.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `owner` - The owner of the source repo
|
||||
- `repo` - The name of the source repo
|
||||
- `title` - The title of the PR
|
||||
- `head` - The branch to send the PR from
|
||||
- `head_repo` - The repo to send the PR from
|
||||
- `base` - The branch to send the PR to
|
||||
- `body` - The body of the PR
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: tasks
|
||||
title: opendevin.action.tasks
|
||||
---
|
||||
|
||||
## TaskStateChangedAction Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TaskStateChangedAction(NotExecutableAction)
|
||||
```
|
||||
|
||||
Fake action, just to notify the client that a task state has changed.
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: opendevin.agent
|
||||
---
|
||||
|
||||
## Agent Objects
|
||||
|
||||
```python
|
||||
class Agent(ABC)
|
||||
```
|
||||
|
||||
This abstract base class is an general interface for an agent dedicated to
|
||||
executing a specific instruction and allowing human interaction with the
|
||||
agent during execution.
|
||||
It tracks the execution status and maintains a history of interactions.
|
||||
|
||||
#### complete
|
||||
|
||||
```python
|
||||
@property
|
||||
def complete() -> bool
|
||||
```
|
||||
|
||||
Indicates whether the current instruction execution is complete.
|
||||
|
||||
**Returns**:
|
||||
|
||||
- complete (bool): True if execution is complete; False otherwise.
|
||||
|
||||
#### step
|
||||
|
||||
```python
|
||||
@abstractmethod
|
||||
def step(state: 'State') -> 'Action'
|
||||
```
|
||||
|
||||
Starts the execution of the assigned instruction. This method should
|
||||
be implemented by subclasses to define the specific execution logic.
|
||||
|
||||
#### search\_memory
|
||||
|
||||
```python
|
||||
@abstractmethod
|
||||
def search_memory(query: str) -> List[str]
|
||||
```
|
||||
|
||||
Searches the agent's memory for information relevant to the given query.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- query (str): The query to search for in the agent's memory.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- response (str): The response to the query.
|
||||
|
||||
#### reset
|
||||
|
||||
```python
|
||||
def reset() -> None
|
||||
```
|
||||
|
||||
Resets the agent's execution status and clears the history. This method can be used
|
||||
to prepare the agent for restarting the instruction or cleaning up before destruction.
|
||||
|
||||
#### register
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def register(cls, name: str, agent_cls: Type['Agent'])
|
||||
```
|
||||
|
||||
Registers an agent class in the registry.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- name (str): The name to register the class under.
|
||||
- agent_cls (Type['Agent']): The class to register.
|
||||
|
||||
|
||||
**Raises**:
|
||||
|
||||
- AgentAlreadyRegisteredError: If name already registered
|
||||
|
||||
#### get\_cls
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def get_cls(cls, name: str) -> Type['Agent']
|
||||
```
|
||||
|
||||
Retrieves an agent class from the registry.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- name (str): The name of the class to retrieve
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- agent_cls (Type['Agent']): The class registered under the specified name.
|
||||
|
||||
|
||||
**Raises**:
|
||||
|
||||
- AgentNotRegisteredError: If name not registered
|
||||
|
||||
#### list\_agents
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def list_agents(cls) -> list[str]
|
||||
```
|
||||
|
||||
Retrieves the list of all agent names from the registry.
|
||||
|
||||
**Raises**:
|
||||
|
||||
- AgentNotRegisteredError: If no agent is registered
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
sidebar_label: config
|
||||
title: opendevin.config
|
||||
---
|
||||
|
||||
#### get
|
||||
|
||||
```python
|
||||
def get(key: ConfigType, required: bool = False)
|
||||
```
|
||||
|
||||
Get a key from the environment variables or config.toml or default configs.
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent_controller
|
||||
title: opendevin.controller.agent_controller
|
||||
---
|
||||
|
||||
## AgentController Objects
|
||||
|
||||
```python
|
||||
class AgentController()
|
||||
```
|
||||
|
||||
#### setup\_task
|
||||
|
||||
```python
|
||||
async def setup_task(task: str, inputs: dict = {})
|
||||
```
|
||||
|
||||
Sets up the agent controller with a task.
|
||||
|
||||
#### start
|
||||
|
||||
```python
|
||||
async def start(task: str)
|
||||
```
|
||||
|
||||
Starts the agent controller with a task.
|
||||
If task already run before, it will continue from the last step.
|
||||
|
||||
#### get\_task\_state
|
||||
|
||||
```python
|
||||
def get_task_state()
|
||||
```
|
||||
|
||||
Returns the current state of the agent task.
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
sidebar_label: files
|
||||
title: opendevin.files
|
||||
---
|
||||
|
||||
## WorkspaceFile Objects
|
||||
|
||||
```python
|
||||
class WorkspaceFile()
|
||||
```
|
||||
|
||||
#### to\_dict
|
||||
|
||||
```python
|
||||
def to_dict() -> Dict[str, Any]
|
||||
```
|
||||
|
||||
Converts the File object to a dictionary.
|
||||
|
||||
**Returns**:
|
||||
|
||||
The dictionary representation of the File object.
|
||||
|
||||
#### get\_folder\_structure
|
||||
|
||||
```python
|
||||
def get_folder_structure(workdir: Path) -> WorkspaceFile
|
||||
```
|
||||
|
||||
Gets the folder structure of a directory.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `workdir` - The directory path.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
The folder structure.
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
sidebar_label: llm
|
||||
title: opendevin.llm.llm
|
||||
---
|
||||
|
||||
## LLM Objects
|
||||
|
||||
```python
|
||||
class LLM()
|
||||
```
|
||||
|
||||
The LLM class represents a Language Model instance.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(model=DEFAULT_MODEL_NAME,
|
||||
api_key=DEFAULT_API_KEY,
|
||||
base_url=DEFAULT_BASE_URL,
|
||||
api_version=DEFAULT_API_VERSION,
|
||||
num_retries=LLM_NUM_RETRIES,
|
||||
retry_min_wait=LLM_RETRY_MIN_WAIT,
|
||||
retry_max_wait=LLM_RETRY_MAX_WAIT,
|
||||
llm_timeout=LLM_TIMEOUT,
|
||||
llm_max_return_tokens=LLM_MAX_RETURN_TOKENS)
|
||||
```
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `model` _str, optional_ - The name of the language model. Defaults to LLM_MODEL.
|
||||
- `api_key` _str, optional_ - The API key for accessing the language model. Defaults to LLM_API_KEY.
|
||||
- `base_url` _str, optional_ - The base URL for the language model API. Defaults to LLM_BASE_URL. Not necessary for OpenAI.
|
||||
- `api_version` _str, optional_ - The version of the API to use. Defaults to LLM_API_VERSION. Not necessary for OpenAI.
|
||||
- `num_retries` _int, optional_ - The number of retries for API calls. Defaults to LLM_NUM_RETRIES.
|
||||
- `retry_min_wait` _int, optional_ - The minimum time to wait between retries in seconds. Defaults to LLM_RETRY_MIN_TIME.
|
||||
- `retry_max_wait` _int, optional_ - The maximum time to wait between retries in seconds. Defaults to LLM_RETRY_MAX_TIME.
|
||||
- `llm_timeout` _int, optional_ - The maximum time to wait for a response in seconds. Defaults to LLM_TIMEOUT.
|
||||
- `llm_max_return_tokens` _int, optional_ - The maximum number of tokens to return. Defaults to LLM_MAX_RETURN_TOKENS.
|
||||
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `model_name` _str_ - The name of the language model.
|
||||
- `api_key` _str_ - The API key for accessing the language model.
|
||||
- `base_url` _str_ - The base URL for the language model API.
|
||||
- `api_version` _str_ - The version of the API to use.
|
||||
|
||||
#### completion
|
||||
|
||||
```python
|
||||
@property
|
||||
def completion()
|
||||
```
|
||||
|
||||
Decorator for the litellm completion function.
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
sidebar_label: logger
|
||||
title: opendevin.logger
|
||||
---
|
||||
|
||||
#### get\_console\_handler
|
||||
|
||||
```python
|
||||
def get_console_handler()
|
||||
```
|
||||
|
||||
Returns a console handler for logging.
|
||||
|
||||
#### get\_file\_handler
|
||||
|
||||
```python
|
||||
def get_file_handler()
|
||||
```
|
||||
|
||||
Returns a file handler for logging.
|
||||
|
||||
#### log\_uncaught\_exceptions
|
||||
|
||||
```python
|
||||
def log_uncaught_exceptions(ex_cls, ex, tb)
|
||||
```
|
||||
|
||||
Logs uncaught exceptions along with the traceback.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ex_cls` _type_ - The type of the exception.
|
||||
- `ex` _Exception_ - The exception instance.
|
||||
- `tb` _traceback_ - The traceback object.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
None
|
||||
|
||||
## LlmFileHandler Objects
|
||||
|
||||
```python
|
||||
class LlmFileHandler(logging.FileHandler)
|
||||
```
|
||||
|
||||
__LLM prompt and response logging__
|
||||
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(filename, mode='a', encoding='utf-8', delay=False)
|
||||
```
|
||||
|
||||
Initializes an instance of LlmFileHandler.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `filename` _str_ - The name of the log file.
|
||||
- `mode` _str, optional_ - The file mode. Defaults to 'a'.
|
||||
- `encoding` _str, optional_ - The file encoding. Defaults to None.
|
||||
- `delay` _bool, optional_ - Whether to delay file opening. Defaults to False.
|
||||
|
||||
#### emit
|
||||
|
||||
```python
|
||||
def emit(record)
|
||||
```
|
||||
|
||||
Emits a log record.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `record` _logging.LogRecord_ - The log record to emit.
|
||||
|
||||
#### get\_llm\_prompt\_file\_handler
|
||||
|
||||
```python
|
||||
def get_llm_prompt_file_handler()
|
||||
```
|
||||
|
||||
Returns a file handler for LLM prompt logging.
|
||||
|
||||
#### get\_llm\_response\_file\_handler
|
||||
|
||||
```python
|
||||
def get_llm_response_file_handler()
|
||||
```
|
||||
|
||||
Returns a file handler for LLM response logging.
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
sidebar_label: main
|
||||
title: opendevin.main
|
||||
---
|
||||
|
||||
#### read\_task\_from\_file
|
||||
|
||||
```python
|
||||
def read_task_from_file(file_path: str) -> str
|
||||
```
|
||||
|
||||
Read task from the specified file.
|
||||
|
||||
#### read\_task\_from\_stdin
|
||||
|
||||
```python
|
||||
def read_task_from_stdin() -> str
|
||||
```
|
||||
|
||||
Read task from stdin.
|
||||
|
||||
#### main
|
||||
|
||||
```python
|
||||
async def main(task_str: str = '')
|
||||
```
|
||||
|
||||
Main coroutine to run the agent controller with task input flexibility.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
sidebar_label: observation
|
||||
title: opendevin.observation
|
||||
---
|
||||
|
||||
#### OBSERVATION\_TYPE\_TO\_CLASS
|
||||
|
||||
type: ignore[attr-defined]
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
sidebar_label: base
|
||||
title: opendevin.observation.base
|
||||
---
|
||||
|
||||
## Observation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Observation()
|
||||
```
|
||||
|
||||
This data class represents an observation of the environment.
|
||||
|
||||
#### to\_dict
|
||||
|
||||
```python
|
||||
def to_dict() -> dict
|
||||
```
|
||||
|
||||
Converts the observation to a dictionary and adds user message.
|
||||
|
||||
#### to\_memory
|
||||
|
||||
```python
|
||||
def to_memory() -> dict
|
||||
```
|
||||
|
||||
Converts the observation to a dictionary.
|
||||
|
||||
#### message
|
||||
|
||||
```python
|
||||
@property
|
||||
def message() -> str
|
||||
```
|
||||
|
||||
Returns a message describing the observation.
|
||||
|
||||
## NullObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class NullObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents a null observation.
|
||||
This is used when the produced action is NOT executable.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: browse
|
||||
title: opendevin.observation.browse
|
||||
---
|
||||
|
||||
## BrowserOutputObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class BrowserOutputObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents the output of a browser.
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
sidebar_label: delegate
|
||||
title: opendevin.observation.delegate
|
||||
---
|
||||
|
||||
## AgentDelegateObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentDelegateObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents a delegate observation.
|
||||
This is used when the produced action is NOT executable.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: error
|
||||
title: opendevin.observation.error
|
||||
---
|
||||
|
||||
## AgentErrorObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentErrorObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents an error encountered by the agent.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
sidebar_label: files
|
||||
title: opendevin.observation.files
|
||||
---
|
||||
|
||||
## FileReadObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FileReadObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents the content of a file.
|
||||
|
||||
## FileWriteObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FileWriteObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents a file write operation
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
sidebar_label: message
|
||||
title: opendevin.observation.message
|
||||
---
|
||||
|
||||
## UserMessageObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class UserMessageObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents a message sent by the user.
|
||||
|
||||
## AgentMessageObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentMessageObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents a message sent by the agent.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: recall
|
||||
title: opendevin.observation.recall
|
||||
---
|
||||
|
||||
## AgentRecallObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentRecallObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents a list of memories recalled by the agent.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: run
|
||||
title: opendevin.observation.run
|
||||
---
|
||||
|
||||
## CmdOutputObservation Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class CmdOutputObservation(Observation)
|
||||
```
|
||||
|
||||
This data class represents the output of a command.
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
---
|
||||
sidebar_label: plan
|
||||
title: opendevin.plan
|
||||
---
|
||||
|
||||
## Task Objects
|
||||
|
||||
```python
|
||||
class Task()
|
||||
```
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(parent: 'Task | None',
|
||||
goal: str,
|
||||
state: str = OPEN_STATE,
|
||||
subtasks: List = [])
|
||||
```
|
||||
|
||||
Initializes a new instance of the Task class.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `parent` - The parent task, or None if it is the root task.
|
||||
- `goal` - The goal of the task.
|
||||
- `state` - The initial state of the task.
|
||||
- `subtasks` - A list of subtasks associated with this task.
|
||||
|
||||
#### to\_string
|
||||
|
||||
```python
|
||||
def to_string(indent='')
|
||||
```
|
||||
|
||||
Returns a string representation of the task and its subtasks.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `indent` - The indentation string for formatting the output.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
A string representation of the task and its subtasks.
|
||||
|
||||
#### to\_dict
|
||||
|
||||
```python
|
||||
def to_dict()
|
||||
```
|
||||
|
||||
Returns a dictionary representation of the task.
|
||||
|
||||
**Returns**:
|
||||
|
||||
A dictionary containing the task's attributes.
|
||||
|
||||
#### set\_state
|
||||
|
||||
```python
|
||||
def set_state(state)
|
||||
```
|
||||
|
||||
Sets the state of the task and its subtasks.
|
||||
|
||||
Args: state: The new state of the task.
|
||||
|
||||
**Raises**:
|
||||
|
||||
- `PlanInvalidStateError` - If the provided state is invalid.
|
||||
|
||||
#### get\_current\_task
|
||||
|
||||
```python
|
||||
def get_current_task() -> 'Task | None'
|
||||
```
|
||||
|
||||
Retrieves the current task in progress.
|
||||
|
||||
**Returns**:
|
||||
|
||||
The current task in progress, or None if no task is in progress.
|
||||
|
||||
## Plan Objects
|
||||
|
||||
```python
|
||||
class Plan()
|
||||
```
|
||||
|
||||
Represents a plan consisting of tasks.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `main_goal` - The main goal of the plan.
|
||||
- `task` - The root task of the plan.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(task: str)
|
||||
```
|
||||
|
||||
Initializes a new instance of the Plan class.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `task` - The main goal of the plan.
|
||||
|
||||
#### \_\_str\_\_
|
||||
|
||||
```python
|
||||
def __str__()
|
||||
```
|
||||
|
||||
Returns a string representation of the plan.
|
||||
|
||||
**Returns**:
|
||||
|
||||
A string representation of the plan.
|
||||
|
||||
#### get\_task\_by\_id
|
||||
|
||||
```python
|
||||
def get_task_by_id(id: str) -> Task
|
||||
```
|
||||
|
||||
Retrieves a task by its ID.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `id` - The ID of the task.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
The task with the specified ID.
|
||||
|
||||
|
||||
**Raises**:
|
||||
|
||||
- `ValueError` - If the provided task ID is invalid or does not exist.
|
||||
|
||||
#### add\_subtask
|
||||
|
||||
```python
|
||||
def add_subtask(parent_id: str, goal: str, subtasks: List = [])
|
||||
```
|
||||
|
||||
Adds a subtask to a parent task.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `parent_id` - The ID of the parent task.
|
||||
- `goal` - The goal of the subtask.
|
||||
- `subtasks` - A list of subtasks associated with the new subtask.
|
||||
|
||||
#### set\_subtask\_state
|
||||
|
||||
```python
|
||||
def set_subtask_state(id: str, state: str)
|
||||
```
|
||||
|
||||
Sets the state of a subtask.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `id` - The ID of the subtask.
|
||||
- `state` - The new state of the subtask.
|
||||
|
||||
#### get\_current\_task
|
||||
|
||||
```python
|
||||
def get_current_task()
|
||||
```
|
||||
|
||||
Retrieves the current task in progress.
|
||||
|
||||
**Returns**:
|
||||
|
||||
The current task in progress, or None if no task is in progress.
|
||||
|
||||
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
---
|
||||
sidebar_label: sandbox
|
||||
title: opendevin.sandbox.e2b.sandbox
|
||||
---
|
||||
|
||||
## E2BBox Objects
|
||||
|
||||
```python
|
||||
class E2BBox(Sandbox)
|
||||
```
|
||||
|
||||
#### copy\_to
|
||||
|
||||
```python
|
||||
def copy_to(host_src: str, sandbox_dest: str, recursive: bool = False)
|
||||
```
|
||||
|
||||
Copies a local file or directory to the sandbox.
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
sidebar_label: jupyter
|
||||
title: opendevin.sandbox.plugins.jupyter
|
||||
---
|
||||
|
||||
## JupyterRequirement Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class JupyterRequirement(PluginRequirement)
|
||||
```
|
||||
|
||||
#### host\_src
|
||||
|
||||
The directory of this file (sandbox/plugins/jupyter)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
sidebar_label: mixin
|
||||
title: opendevin.sandbox.plugins.mixin
|
||||
---
|
||||
|
||||
## PluginMixin Objects
|
||||
|
||||
```python
|
||||
class PluginMixin()
|
||||
```
|
||||
|
||||
Mixin for Sandbox to support plugins.
|
||||
|
||||
#### init\_plugins
|
||||
|
||||
```python
|
||||
def init_plugins(requirements: List[PluginRequirement])
|
||||
```
|
||||
|
||||
Load a plugin into the sandbox.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
sidebar_label: requirement
|
||||
title: opendevin.sandbox.plugins.requirement
|
||||
---
|
||||
|
||||
## PluginRequirement Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class PluginRequirement()
|
||||
```
|
||||
|
||||
Requirement for a plugin.
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
sidebar_label: action
|
||||
title: opendevin.schema.action
|
||||
---
|
||||
|
||||
## ActionTypeSchema Objects
|
||||
|
||||
```python
|
||||
class ActionTypeSchema(BaseModel)
|
||||
```
|
||||
|
||||
#### INIT
|
||||
|
||||
Initializes the agent. Only sent by client.
|
||||
|
||||
#### START
|
||||
|
||||
Starts a new development task. Only sent by the client.
|
||||
|
||||
#### READ
|
||||
|
||||
Reads the content of a file.
|
||||
|
||||
#### WRITE
|
||||
|
||||
Writes the content to a file.
|
||||
|
||||
#### RUN
|
||||
|
||||
Runs a command.
|
||||
|
||||
#### KILL
|
||||
|
||||
Kills a background command.
|
||||
|
||||
#### BROWSE
|
||||
|
||||
Opens a web page.
|
||||
|
||||
#### RECALL
|
||||
|
||||
Searches long-term memory
|
||||
|
||||
#### THINK
|
||||
|
||||
Allows the agent to make a plan, set a goal, or record thoughts
|
||||
|
||||
#### DELEGATE
|
||||
|
||||
Delegates a task to another agent.
|
||||
|
||||
#### FINISH
|
||||
|
||||
If you're absolutely certain that you've completed your task and have tested your work,
|
||||
use the finish action to stop working.
|
||||
|
||||
#### PAUSE
|
||||
|
||||
Pauses the task.
|
||||
|
||||
#### RESUME
|
||||
|
||||
Resumes the task.
|
||||
|
||||
#### STOP
|
||||
|
||||
Stops the task. Must send a start action to restart a new task.
|
||||
|
||||
#### PUSH
|
||||
|
||||
Push a branch to github.
|
||||
|
||||
#### SEND\_PR
|
||||
|
||||
Send a PR to github.
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
sidebar_label: observation
|
||||
title: opendevin.schema.observation
|
||||
---
|
||||
|
||||
## ObservationTypeSchema Objects
|
||||
|
||||
```python
|
||||
class ObservationTypeSchema(BaseModel)
|
||||
```
|
||||
|
||||
#### READ
|
||||
|
||||
The content of a file
|
||||
|
||||
#### BROWSE
|
||||
|
||||
The HTML content of a URL
|
||||
|
||||
#### RUN
|
||||
|
||||
The output of a command
|
||||
|
||||
#### RECALL
|
||||
|
||||
The result of a search
|
||||
|
||||
#### CHAT
|
||||
|
||||
A message from the user
|
||||
|
||||
#### DELEGATE
|
||||
|
||||
The result of a task delegated to another agent
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
sidebar_label: task
|
||||
title: opendevin.schema.task
|
||||
---
|
||||
|
||||
## TaskState Objects
|
||||
|
||||
```python
|
||||
class TaskState(str, Enum)
|
||||
```
|
||||
|
||||
#### INIT
|
||||
|
||||
Initial state of the task.
|
||||
|
||||
#### RUNNING
|
||||
|
||||
The task is running.
|
||||
|
||||
#### PAUSED
|
||||
|
||||
The task is paused.
|
||||
|
||||
#### STOPPED
|
||||
|
||||
The task is stopped.
|
||||
|
||||
#### FINISHED
|
||||
|
||||
The task is finished.
|
||||
|
||||
#### ERROR
|
||||
|
||||
An error occurred during the task.
|
||||
|
||||
## TaskStateAction Objects
|
||||
|
||||
```python
|
||||
class TaskStateAction(str, Enum)
|
||||
```
|
||||
|
||||
#### START
|
||||
|
||||
Starts the task.
|
||||
|
||||
#### PAUSE
|
||||
|
||||
Pauses the task.
|
||||
|
||||
#### RESUME
|
||||
|
||||
Resumes the task.
|
||||
|
||||
#### STOP
|
||||
|
||||
Stops the task.
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
---
|
||||
sidebar_label: agent
|
||||
title: opendevin.server.agent.agent
|
||||
---
|
||||
|
||||
## AgentUnit Objects
|
||||
|
||||
```python
|
||||
class AgentUnit()
|
||||
```
|
||||
|
||||
Represents a session with an agent.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `controller` - The AgentController instance for controlling the agent.
|
||||
- `agent_task` - The task representing the agent's execution.
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(sid)
|
||||
```
|
||||
|
||||
Initializes a new instance of the Session class.
|
||||
|
||||
#### send\_error
|
||||
|
||||
```python
|
||||
async def send_error(message)
|
||||
```
|
||||
|
||||
Sends an error message to the client.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `message` - The error message to send.
|
||||
|
||||
#### send\_message
|
||||
|
||||
```python
|
||||
async def send_message(message)
|
||||
```
|
||||
|
||||
Sends a message to the client.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `message` - The message to send.
|
||||
|
||||
#### send
|
||||
|
||||
```python
|
||||
async def send(data)
|
||||
```
|
||||
|
||||
Sends data to the client.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `data` - The data to send.
|
||||
|
||||
#### dispatch
|
||||
|
||||
```python
|
||||
async def dispatch(action: str | None, data: dict)
|
||||
```
|
||||
|
||||
Dispatches actions to the agent from the client.
|
||||
|
||||
#### get\_arg\_or\_default
|
||||
|
||||
```python
|
||||
def get_arg_or_default(_args: dict, key: ConfigType) -> str
|
||||
```
|
||||
|
||||
Gets an argument from the args dictionary or the default value.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `_args` - The args dictionary.
|
||||
- `key` - The key to get.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
The value of the key or the default value.
|
||||
|
||||
#### create\_controller
|
||||
|
||||
```python
|
||||
async def create_controller(start_event: dict)
|
||||
```
|
||||
|
||||
Creates an AgentController instance.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `start_event` - The start event data (optional).
|
||||
|
||||
#### start\_task
|
||||
|
||||
```python
|
||||
async def start_task(start_event)
|
||||
```
|
||||
|
||||
Starts a task for the agent.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `start_event` - The start event data.
|
||||
|
||||
#### set\_task\_state
|
||||
|
||||
```python
|
||||
async def set_task_state(new_state_action: TaskStateAction)
|
||||
```
|
||||
|
||||
Sets the state of the agent task.
|
||||
|
||||
#### on\_agent\_event
|
||||
|
||||
```python
|
||||
async def on_agent_event(event: Observation | Action)
|
||||
```
|
||||
|
||||
Callback function for agent events.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `event` - The agent event (Observation or Action).
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
sidebar_label: manager
|
||||
title: opendevin.server.agent.manager
|
||||
---
|
||||
|
||||
## AgentManager Objects
|
||||
|
||||
```python
|
||||
class AgentManager()
|
||||
```
|
||||
|
||||
#### register\_agent
|
||||
|
||||
```python
|
||||
def register_agent(sid: str)
|
||||
```
|
||||
|
||||
Registers a new agent.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `sid` - The session ID of the agent.
|
||||
|
||||
#### dispatch
|
||||
|
||||
```python
|
||||
async def dispatch(sid: str, action: str | None, data: dict)
|
||||
```
|
||||
|
||||
Dispatches actions to the agent from the client.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
sidebar_label: auth
|
||||
title: opendevin.server.auth.auth
|
||||
---
|
||||
|
||||
#### get\_sid\_from\_token
|
||||
|
||||
```python
|
||||
def get_sid_from_token(token: str) -> str
|
||||
```
|
||||
|
||||
Retrieves the session id from a JWT token.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `token` _str_ - The JWT token from which the session id is to be extracted.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- `str` - The session id if found and valid, otherwise an empty string.
|
||||
|
||||
#### sign\_token
|
||||
|
||||
```python
|
||||
def sign_token(payload: Dict[str, object]) -> str
|
||||
```
|
||||
|
||||
Signs a JWT token.
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
sidebar_label: listen
|
||||
title: opendevin.server.listen
|
||||
---
|
||||
|
||||
#### get\_litellm\_models
|
||||
|
||||
```python
|
||||
@app.get('/api/litellm-models')
|
||||
async def get_litellm_models()
|
||||
```
|
||||
|
||||
Get all models supported by LiteLLM.
|
||||
|
||||
#### get\_agents
|
||||
|
||||
```python
|
||||
@app.get('/api/agents')
|
||||
async def get_agents()
|
||||
```
|
||||
|
||||
Get all agents supported by LiteLLM.
|
||||
|
||||
#### get\_token
|
||||
|
||||
```python
|
||||
@app.get('/api/auth')
|
||||
async def get_token(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security_scheme))
|
||||
```
|
||||
|
||||
Generate a JWT for authentication when starting a WebSocket connection. This endpoint checks if valid credentials
|
||||
are provided and uses them to get a session ID. If no valid credentials are provided, it generates a new session ID.
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
sidebar_label: manager
|
||||
title: opendevin.server.session.manager
|
||||
---
|
||||
|
||||
## SessionManager Objects
|
||||
|
||||
```python
|
||||
class SessionManager()
|
||||
```
|
||||
|
||||
#### send
|
||||
|
||||
```python
|
||||
async def send(sid: str, data: Dict[str, object]) -> bool
|
||||
```
|
||||
|
||||
Sends data to the client.
|
||||
|
||||
#### send\_error
|
||||
|
||||
```python
|
||||
async def send_error(sid: str, message: str) -> bool
|
||||
```
|
||||
|
||||
Sends an error message to the client.
|
||||
|
||||
#### send\_message
|
||||
|
||||
```python
|
||||
async def send_message(sid: str, message: str) -> bool
|
||||
```
|
||||
|
||||
Sends a message to the client.
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
sidebar_label: msg_stack
|
||||
title: opendevin.server.session.msg_stack
|
||||
---
|
||||
|
||||
## Message Objects
|
||||
|
||||
```python
|
||||
class Message()
|
||||
```
|
||||
|
||||
#### role
|
||||
|
||||
"user"| "assistant"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user