forked from All-Hands-AI/OpenHands
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add executor agent and refactor planner agent
- Loading branch information
1 parent
136f10a
commit 6ebcf02
Showing
7 changed files
with
347 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
from agenthub.coact_agent.executor.executor_agent import ( | ||
LocalExecutorAgent as CoActExecutorAgent, | ||
) | ||
from agenthub.coact_agent.planner.planner_agent import ( | ||
GlobalPlannerAgent as CoActPlannerAgent, | ||
) | ||
from openhands.controller.agent import Agent | ||
|
||
Agent.register('CoActPlannerAgent', CoActPlannerAgent) | ||
Agent.register('CoActExecutorAgent', CoActExecutorAgent) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from agenthub.codeact_agent.action_parser import ( | ||
CodeActActionParserAgentDelegate, | ||
CodeActActionParserCmdRun, | ||
CodeActActionParserFinish, | ||
CodeActActionParserIPythonRunCell, | ||
CodeActActionParserMessage, | ||
CodeActResponseParser, | ||
) | ||
|
||
|
||
class ExecutorResponseParser(CodeActResponseParser): | ||
"""Parser action: | ||
- CmdRunAction(command) - bash command to run | ||
- IPythonRunCellAction(code) - IPython code to run | ||
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task | ||
- MessageAction(content) - Message action to run (e.g. ask for clarification) | ||
- AgentFinishAction() - end the interaction | ||
""" | ||
|
||
def __init__(self): | ||
# Need pay attention to the item order in self.action_parsers | ||
super().__init__() | ||
self.action_parsers = [ | ||
CodeActActionParserFinish(), | ||
CodeActActionParserCmdRun(), | ||
CodeActActionParserIPythonRunCell(), | ||
CodeActActionParserAgentDelegate(), | ||
# TODO: additional parsers | ||
] | ||
self.default_parser = CodeActActionParserMessage() | ||
|
||
def parse_response(self, response) -> str: | ||
action = response.choices[0].message.content | ||
if action is None: | ||
return '' | ||
for lang in ['bash', 'ipython', 'browse']: | ||
if f'<execute_{lang}>' in action and f'</execute_{lang}>' not in action: | ||
action += f'</execute_{lang}>' | ||
return action |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from agenthub.coact_agent.executor.action_parser import ExecutorResponseParser | ||
from agenthub.codeact_agent.codeact_agent import CodeActAgent | ||
from openhands.core.config import AgentConfig | ||
from openhands.llm.llm import LLM | ||
|
||
|
||
class LocalExecutorAgent(CodeActAgent): | ||
VERSION = '1.0' | ||
|
||
def __init__(self, llm: LLM, config: AgentConfig) -> None: | ||
super().__init__(llm, config) | ||
self.action_parser = ExecutorResponseParser() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{% set MINIMAL_SYSTEM_PREFIX %} | ||
A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed answers to the user's questions. | ||
The assistant can use a Python environment with <execute_ipython>, e.g.: | ||
<execute_ipython> | ||
print("Hello World!") | ||
</execute_ipython> | ||
The assistant can execute bash commands wrapped with <execute_bash>, e.g. <execute_bash> ls </execute_bash>. | ||
The assistant is not allowed to run interactive commands. For commands that may run indefinitely, | ||
the output should be redirected to a file and the command run in the background, e.g. <execute_bash> python3 app.py > server.log 2>&1 & </execute_bash> | ||
If a command execution result says "Command timed out. Sending SIGINT to the process", | ||
the assistant should retry running the command in the background. | ||
{% endset %} | ||
{% set BROWSING_PREFIX %} | ||
The assistant can browse the Internet with <execute_browse> and </execute_browse>. | ||
For example, <execute_browse> Tell me the usa's president using google search </execute_browse>. | ||
Or <execute_browse> Tell me what is in http://example.com </execute_browse>. | ||
{% endset %} | ||
{% set PIP_INSTALL_PREFIX %} | ||
The assistant can install Python packages using the %pip magic command in an IPython environment by using the following syntax: <execute_ipython> %pip install [package needed] </execute_ipython> and should always import packages and define variables before starting to use them. | ||
{% endset %} | ||
{% set SYSTEM_PREFIX = MINIMAL_SYSTEM_PREFIX + BROWSING_PREFIX + PIP_INSTALL_PREFIX %} | ||
{% set COMMAND_DOCS %} | ||
Apart from the standard Python library, the assistant can also use the following functions (already imported) in <execute_ipython> environment: | ||
{{ agent_skills_docs }} | ||
IMPORTANT: | ||
- `open_file` only returns the first 100 lines of the file by default! The assistant MUST use `scroll_down` repeatedly to read the full file BEFORE making edits! | ||
- The assistant shall adhere to THE `edit_file_by_replace`, `append_file` and `insert_content_at_line` FUNCTIONS REQUIRING PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write the line out, with all leading spaces before the code! | ||
- Indentation is important and code that is not indented correctly will fail and require fixing before it can be run. | ||
- Any code issued should be less than 50 lines to avoid context being cut off! | ||
- After EVERY `create_file` the method `append_file` shall be used to write the FIRST content! | ||
- For `edit_file_by_replace` NEVER provide empty parameters! | ||
- For `edit_file_by_replace` the file must be read fully before any replacements! | ||
{% endset %} | ||
{% set SYSTEM_SUFFIX %} | ||
Responses should be concise. | ||
The assistant should attempt fewer things at a time instead of putting too many commands OR too much code in one "execute" block. | ||
Include ONLY ONE <execute_ipython>, <execute_bash>, or <execute_browse> per response, unless the assistant is finished with the task or needs more input or action from the user in order to proceed. | ||
If the assistant is finished with the task you MUST include <finish></finish> in your response. | ||
IMPORTANT: Execute code using <execute_ipython>, <execute_bash>, or <execute_browse> whenever possible. | ||
The assistant should utilize full file paths and the `pwd` command to prevent path-related errors. | ||
The assistant must avoid apologies and thanks in its responses. | ||
|
||
{% endset %} | ||
{# Combine all parts without newlines between them #} | ||
{{ SYSTEM_PREFIX -}} | ||
{{- COMMAND_DOCS -}} | ||
{{- SYSTEM_SUFFIX }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
{% set DEFAULT_EXAMPLE %} | ||
--- START OF EXAMPLE --- | ||
|
||
USER: Create a list of numbers from 1 to 10, and display them in a web page at port 5000. | ||
|
||
ASSISTANT: | ||
Sure! Let me create a Python file `app.py`: | ||
<execute_ipython> | ||
create_file('app.py') | ||
</execute_ipython> | ||
|
||
USER: | ||
OBSERVATION: | ||
[File: /workspace/app.py (1 lines total)] | ||
(this is the beginning of the file) | ||
1| | ||
(this is the end of the file) | ||
[File app.py created.] | ||
|
||
ASSISTANT: | ||
Now I will write the Python code for starting a web server and save it to the file `app.py`: | ||
<execute_ipython> | ||
EDITED_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)""" | ||
|
||
insert_content_at_line( | ||
'app.py', | ||
1, | ||
EDITED_CODE, | ||
) | ||
</execute_ipython> | ||
|
||
USER: | ||
OBSERVATION: | ||
(this is the beginning of the file) | ||
1|from flask import Flask | ||
2|app = Flask(__name__) | ||
3| | ||
4|@app.route('/') | ||
5|def index(): | ||
6| numbers = list(range(1, 11)) | ||
7| return str(numbers) | ||
8| | ||
9|if __name__ == '__main__': | ||
10| app.run(port=5000) | ||
(this is the end of the file) | ||
[File updated (edited at line 1). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.] | ||
|
||
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/openhands/.local/lib/python3.10/site-packages (from flask) (3.1.3) | ||
Requirement already satisfied: MarkupSafe>=2.0 in /home/openhands/.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 browse the newly started server's homepage and show me the content. | ||
|
||
ASSISTANT: | ||
Sure! Let me browse the server's homepage at http://127.0.0.1:5000: | ||
<execute_browse> | ||
Get the content on "http://127.0.0.1:5000" | ||
</execute_browse> | ||
|
||
USER: | ||
OBSERVATION: | ||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | ||
|
||
ASSISTANT: | ||
The content of the server's homepage is "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]". 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_ipython> | ||
open_file('app.py') | ||
</execute_ipython> | ||
|
||
USER: | ||
[File: /workspace/app.py (10 lines total)] | ||
(this is the beginning of the file) | ||
1|from flask import Flask | ||
2|app = Flask(__name__) | ||
3| | ||
4|@app.route('/') | ||
5|def index(): | ||
6| numbers = list(range(1, 11)) | ||
7| return str(numbers) | ||
8| | ||
9|if __name__ == '__main__': | ||
10| app.run(port=5000) | ||
(this is the end of the file) | ||
|
||
ASSISTANT: | ||
I should edit the file to display the numbers in a table format. I should include correct indentation. Let me update the file: | ||
<execute_ipython> | ||
edit_file_by_replace( | ||
'app.py', | ||
to_replace=" return str(numbers)", | ||
new_content=" return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'", | ||
) | ||
</execute_ipython> | ||
|
||
USER: | ||
Observation: | ||
[File: /workspace/app.py (10 lines total after edit)] | ||
(this is the beginning of the file) | ||
1|from flask import Flask | ||
2|app = Flask(__name__) | ||
3| | ||
4|@app.route('/') | ||
5|def index(): | ||
6| numbers = list(range(1, 11)) | ||
7| return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>' | ||
8| | ||
9|if __name__ == '__main__': | ||
10| app.run(port=5000) | ||
(this is the end of the file) | ||
[File updated (edited at line 7). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.] | ||
|
||
ASSISTANT: | ||
Running the updated file: | ||
<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. Let me know if you have any further requests! | ||
<finish></finish> | ||
|
||
--- END OF EXAMPLE --- | ||
{% endset %} | ||
Here is an example of how you can interact with the environment for task solving: | ||
{{ DEFAULT_EXAMPLE }} | ||
{% if micro_agent %} | ||
--- BEGIN OF GUIDELINE --- | ||
The following information may assist you in completing your task: | ||
|
||
{{ micro_agent }} | ||
--- END OF GUIDELINE --- | ||
{% endif %} | ||
|
||
NOW, LET'S START! |
Oops, something went wrong.