Skip to content

Commit 435f47c

Browse files
authored
Improve the both frontend and backend for CodeActAgent (OpenHands#1494)
* improve the both frontend and backend for CodeActAgent * fix linter * update integration test
1 parent 545327c commit 435f47c

16 files changed

Lines changed: 65 additions & 291 deletions

File tree

agenthub/codeact_agent/codeact_agent.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ def parse_response(response) -> str:
3434
action += f'</execute_{lang}>'
3535
return action
3636

37+
def truncate_observation(observation: str, max_chars: int=5000) -> str:
38+
"""
39+
Truncate the middle of the observation if it is too long.
40+
"""
41+
if len(observation) <= max_chars:
42+
return observation
43+
half = max_chars // 2
44+
return observation[:half] + '\n[... Observation truncated due to length ...]\n' + observation[-half:]
3745

3846
class CodeActAgent(Agent):
3947
"""
@@ -117,9 +125,10 @@ def step(self, state: State) -> Action:
117125
if obs.content.strip() == '/exit':
118126
return AgentFinishAction()
119127
elif isinstance(obs, CmdOutputObservation):
120-
content = 'OBSERVATION:\n' + obs.content
128+
content = 'OBSERVATION:\n' + truncate_observation(obs.content)
121129
content += f'\n[Command {obs.command_id} finished with exit code {obs.exit_code}]]'
122130
self.messages.append({'role': 'user', 'content': content})
131+
123132
elif isinstance(obs, IPythonRunCellObservation):
124133
content = 'OBSERVATION:\n' + obs.content
125134
# replace base64 images with a placeholder
@@ -128,6 +137,7 @@ def step(self, state: State) -> Action:
128137
if '![image](data:image/png;base64,' in line:
129138
splited[i] = '![image](data:image/png;base64, ...) already displayed to user'
130139
content = '\n'.join(splited)
140+
content = truncate_observation(content)
131141
self.messages.append({'role': 'user', 'content': content})
132142
else:
133143
raise NotImplementedError(

agenthub/codeact_agent/prompt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
4141
{_COMMAND_DOCS}
4242
43-
Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
4443
The assistant's response should be concise, but do express their thoughts.
4544
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.
45+
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
4646
"""
4747

4848
EXAMPLES = """

docs/modules/python/agenthub/codeact_agent/codeact_agent.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ sidebar_label: codeact_agent
33
title: agenthub.codeact_agent.codeact_agent
44
---
55

6+
#### truncate\_observation
7+
8+
```python
9+
def truncate_observation(observation: str, max_chars: int = 5000) -> str
10+
```
11+
12+
Truncate the middle of the observation if it is too long.
13+
614
## CodeActAgent Objects
715

816
```python

frontend/src/components/Jupyter.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,44 @@ function JupyterCell({ cell }: IJupyterCell): JSX.Element {
2828
</div>
2929
);
3030
}
31+
32+
// aggregate all the NON-image lines into a single plaintext.
33+
const lines: { type: "plaintext" | "image"; content: string }[] = [];
34+
let current = "";
35+
for (const line of code.split("\n")) {
36+
if (line.startsWith("![image](data:image/png;base64,")) {
37+
lines.push({ type: "plaintext", content: current });
38+
lines.push({ type: "image", content: line });
39+
current = "";
40+
} else {
41+
current += `${line}\n`;
42+
}
43+
}
44+
lines.push({ type: "plaintext", content: current });
45+
3146
return (
3247
<div className="rounded-lg bg-gray-800 dark:bg-gray-900 p-2 text-xs">
3348
<div className="mb-1 text-gray-400">STDOUT/STDERR</div>
3449
<pre
3550
className="scrollbar-custom scrollbar-thumb-gray-500 hover:scrollbar-thumb-gray-400 dark:scrollbar-thumb-white/10 dark:hover:scrollbar-thumb-white/20 overflow-auto px-5 max-h-[60vh] bg-gray-800"
3651
style={{ padding: 0, marginBottom: 0, fontSize: "0.75rem" }}
3752
>
38-
{/* split code by newline and render each line as a plaintext, except it starts with `![image]` so we render it as markdown */}
39-
{code.split("\n").map((line, index) => {
40-
if (line.startsWith("![image](data:image/png;base64,")) {
41-
// add new line before and after the image
53+
{/* display the lines as plaintext or image */}
54+
{lines.map((line, index) => {
55+
if (line.type === "image") {
4256
return (
4357
<div key={index}>
4458
<Markdown urlTransform={(value: string) => value}>
45-
{line}
59+
{line.content}
4660
</Markdown>
47-
<br />
4861
</div>
4962
);
5063
}
5164
return (
5265
<div key={index}>
5366
<SyntaxHighlighter language="plaintext" style={atomOneDark}>
54-
{line}
67+
{line.content}
5568
</SyntaxHighlighter>
56-
<br />
5769
</div>
5870
);
5971
})}

frontend/src/components/file-explorer/FileExplorer.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import {
66
IoIosCloudUpload,
77
} from "react-icons/io";
88
import { twMerge } from "tailwind-merge";
9-
import { WorkspaceFile, getWorkspace, uploadFile } from "#/services/fileService";
9+
import {
10+
WorkspaceFile,
11+
getWorkspace,
12+
uploadFile,
13+
} from "#/services/fileService";
1014
import IconButton from "../IconButton";
1115
import ExplorerTree from "./ExplorerTree";
1216
import { removeEmptyNodes } from "./utils";

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ function SettingsModal({ isOpen, onOpenChange }: SettingsProps) {
7676
i18next.changeLanguage(settings.LANGUAGE);
7777
initializeAgent(settings); // reinitialize the agent with the new settings
7878

79-
const sensitiveKeys = ['LLM_API_KEY'];
79+
const sensitiveKeys = ["LLM_API_KEY"];
8080

8181
Object.entries(updatedSettings).forEach(([key, value]) => {
82-
if (!sensitiveKeys.includes(key)) {
83-
toast.settingsChanged(`${key} set to "${value}"`);
84-
} else {
85-
toast.settingsChanged(`${key} has been updated securely.`);
86-
}
82+
if (!sensitiveKeys.includes(key)) {
83+
toast.settingsChanged(`${key} set to "${value}"`);
84+
} else {
85+
toast.settingsChanged(`${key} has been updated securely.`);
86+
}
8787
});
8888

8989
localStorage.setItem(

tests/integration/mock/CodeActAgent/test_write_simple_script/prompt_001.log

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ edit <start_line>:<end_line>
2828
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.
2929
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.
3030

31-
Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3231
The assistant's response should be concise, but do express their thoughts.
3332
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.
33+
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3434

3535

3636
----------

tests/integration/mock/CodeActAgent/test_write_simple_script/prompt_002.log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ edit <start_line>:<end_line>
2828
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.
2929
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.
3030

31-
Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3231
The assistant's response should be concise, but do express their thoughts.
3332
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.
33+
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3434

3535

3636
----------
@@ -216,7 +216,7 @@ Write a shell script 'hello.sh' that prints 'hello'.
216216

217217
----------
218218

219-
Let's start by creating the shell script `hello.sh` that prints "hello". I'll write the script and save it to a file.
219+
Sure, I'll create a shell script named `hello.sh` that prints "hello". Let's start by writing the script.
220220

221221
<execute_bash>
222222
echo "echo 'hello'" > hello.sh

tests/integration/mock/CodeActAgent/test_write_simple_script/prompt_003.log

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ edit <start_line>:<end_line>
2828
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.
2929
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.
3030

31-
Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3231
The assistant's response should be concise, but do express their thoughts.
3332
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.
33+
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3434

3535

3636
----------
@@ -216,7 +216,7 @@ Write a shell script 'hello.sh' that prints 'hello'.
216216

217217
----------
218218

219-
Let's start by creating the shell script `hello.sh` that prints "hello". I'll write the script and save it to a file.
219+
Sure, I'll create a shell script named `hello.sh` that prints "hello". Let's start by writing the script.
220220

221221
<execute_bash>
222222
echo "echo 'hello'" > hello.sh
@@ -230,7 +230,7 @@ OBSERVATION:
230230

231231
----------
232232

233-
The shell script `hello.sh` has been successfully created. Would you like to run the script to see the output, or is there anything else you'd like to do with it?
233+
The shell script `hello.sh` has been successfully created. If you'd like, I can run the script to show you the output. Would you like me to proceed with executing the script?
234234

235235
----------
236236

tests/integration/mock/CodeActAgent/test_write_simple_script/prompt_004.log

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ edit <start_line>:<end_line>
2828
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.
2929
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.
3030

31-
Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3231
The assistant's response should be concise, but do express their thoughts.
3332
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.
33+
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
3434

3535

3636
----------
@@ -216,7 +216,7 @@ Write a shell script 'hello.sh' that prints 'hello'.
216216

217217
----------
218218

219-
Let's start by creating the shell script `hello.sh` that prints "hello". I'll write the script and save it to a file.
219+
Sure, I'll create a shell script named `hello.sh` that prints "hello". Let's start by writing the script.
220220

221221
<execute_bash>
222222
echo "echo 'hello'" > hello.sh
@@ -230,15 +230,15 @@ OBSERVATION:
230230

231231
----------
232232

233-
The shell script `hello.sh` has been successfully created. Would you like to run the script to see the output, or is there anything else you'd like to do with it?
233+
The shell script `hello.sh` has been successfully created. If you'd like, I can run the script to show you the output. Would you like me to proceed with executing the script?
234234

235235
----------
236236

237237
can you run it to make sure it is working?
238238

239239
----------
240240

241-
Sure, let's run the script `hello.sh` to ensure it's working correctly.
241+
Certainly! Let's run the `hello.sh` script to ensure it's working as expected.
242242

243243
<execute_bash>
244244
bash hello.sh

0 commit comments

Comments
 (0)