Skip to content

Commit

Permalink
Add test for swe actions (#214)
Browse files Browse the repository at this point in the history
### **PR Type**
Tests, Enhancement


___

### **Description**
- Added a command to get the remote URL of the origin before fetching
and resetting in `clone_github.py`.
- Expanded the test in `test_workspace.py` to cover a comprehensive Git
workflow including cloning, opening, editing, and resetting files.
- Standardized string formatting to use double quotes and improved HTML
content generation in `visualize.py`.
  • Loading branch information
kaavee315 committed Jun 24, 2024
1 parent 99e0c21 commit 7f80125
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def reset_to_base_commit(self, request_data: GithubCloneRequest) -> None:
self._setup(request_data)
# repo_dir = request_data.repo_name.split("/")[-1].strip()
reset_commands = [
"git remote get-url origin",
"git fetch --all",
f"git reset --hard {request_data.commit_id}",
"git clean -fdx",
Expand Down
134 changes: 132 additions & 2 deletions python/composio/local_tools/local_workspace/tests/test_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@

import pytest

from composio.local_tools.local_workspace.cmd_manager.actions.clone_github import (
GithubCloneCmd,
GithubCloneRequest,
)
from composio.local_tools.local_workspace.cmd_manager.actions.cmds import (
OpenCmdRequest,
OpenFile,
)
from composio.local_tools.local_workspace.cmd_manager.actions.edit_cmd import (
EditFile,
EditFileRequest,
)
from composio.local_tools.local_workspace.cmd_manager.actions.get_patch import (
GetPatchCmd,
GetPatchRequest,
)
from composio.local_tools.local_workspace.cmd_manager.actions.search_cmds import (
GetCurrentDirCmd,
GetCurrentDirRequest,
Expand All @@ -24,8 +40,8 @@
condition=os.environ.get("CI") is not None,
reason="no way of currently testing this in github action",
)
class TestCreateWorkspaceAction(unittest.TestCase):
def test_create_workspace(self):
class TestWorkspaceGitWorkflow(unittest.TestCase):
def test_git_workflow(self):
# Setup - create an instance of CreateWorkspaceAction
w = WorkspaceManagerFactory()
h = HistoryProcessor()
Expand All @@ -39,6 +55,120 @@ def test_create_workspace(self):

# Verify - Check if the workspace was created successfully
self.assertIsNotNone(result.workspace_id)
workspace_id = result.workspace_id

action = GithubCloneCmd()
action.set_workspace_and_history(w, h)
github_clone_result = action.execute(
GithubCloneRequest(
repo_name="kaavee315/ML_assignment",
workspace_id=workspace_id,
commit_id="",
just_reset=False,
),
{},
)
self.assertIsNotNone(github_clone_result)

action = GithubCloneCmd()
action.set_workspace_and_history(w, h)
github_clone_result = action.execute(
GithubCloneRequest(
repo_name="kaavee315/ML_assignment",
workspace_id=workspace_id,
commit_id="",
just_reset=False,
),
{},
)
self.assertIsNotNone(github_clone_result)

action = OpenFile()
action.set_workspace_and_history(w, h)
open_file_result = action.execute(
OpenCmdRequest(
file_name="README.md",
workspace_id=workspace_id,
),
{},
)
self.assertIsNotNone(open_file_result)
print("Open File 1 result: ", open_file_result)

action = EditFile()
action.set_workspace_and_history(w, h)
edit_file_result = action.execute(
EditFileRequest(
start_line=1,
end_line=1,
replacement_text="print('Hello, World!')",
workspace_id=workspace_id,
),
{},
)
self.assertIsNotNone(edit_file_result)
print("Edit File result: ", edit_file_result)

action = OpenFile()
action.set_workspace_and_history(w, h)
open_file_result = action.execute(
OpenCmdRequest(
file_name="README.md",
workspace_id=workspace_id,
),
{},
)
self.assertIsNotNone(open_file_result)
print("Open File 2 result: ", open_file_result)

action = GetPatchCmd()
action.set_workspace_and_history(w, h)
get_patch_result = action.execute(
GetPatchRequest(
workspace_id=workspace_id,
),
{},
)
self.assertIsNotNone(get_patch_result)
self.assertIsInstance(get_patch_result, tuple)
self.assertIsInstance(tuple(get_patch_result)[0], tuple)
patch_content = (
tuple(tuple(get_patch_result)[0])[1]
if isinstance(tuple(tuple(get_patch_result)[0])[1], str)
else str(tuple(tuple(get_patch_result)[0])[1])
)
self.assertIn("Hello", patch_content)
self.assertIn("README", patch_content)
self.assertIn("diff", patch_content)

action = GithubCloneCmd()
action.set_workspace_and_history(w, h)
github_reset_result = action.execute(
GithubCloneRequest(
repo_name="kaavee315/ML_assignment",
workspace_id=workspace_id,
commit_id="",
just_reset=True,
),
{},
)
print("Github Reset result: ", github_reset_result)
self.assertIsNotNone(github_reset_result)

action = OpenFile()
action.set_workspace_and_history(w, h)
open_file_result = action.execute(
OpenCmdRequest(
file_name="README.md",
workspace_id=workspace_id,
),
{},
)
self.assertIsNotNone(open_file_result)
print("Open File result: ", open_file_result)

# Check that the file content doesn't contain "Hello, World!"
self.assertNotIn("Hello", open_file_result)


@pytest.mark.skipif(
Expand Down
101 changes: 53 additions & 48 deletions python/examples/swe/evaluation/visualize.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import json
import sys


def load_logs(file_path):
with open(file_path, 'r') as file:
with open(file_path, "r") as file:
return json.load(file) # Load the entire JSON file containing all issues


Expand All @@ -11,27 +12,29 @@ def extract_details(agent_logs):
for log in agent_logs:
# from pprint import pprint
# pprint(log)
if log['agent_action'] == 'agent_finish':
if log["agent_action"] == "agent_finish":
agent_action = "agent_finish"
agent_output = log['agent_output']
agent_output = log["agent_output"]
else:
agent_action = json.loads(log['agent_action'])
tool_name = agent_action.get('tool', 'N/A')
tool_input = agent_action.get('tool_input', 'N/A')
tool_output = log.get('tool_output', 'N/A')
agent_thought = agent_action.get('log', 'No thoughts recorded')

data.append({
'tool_name': tool_name or agent_action,
'tool_input': tool_input,
'tool_output': tool_output or agent_output,
'agent_thought': agent_thought
})
agent_action = json.loads(log["agent_action"])
tool_name = agent_action.get("tool", "N/A")
tool_input = agent_action.get("tool_input", "N/A")
tool_output = log.get("tool_output", "N/A")
agent_thought = agent_action.get("log", "No thoughts recorded")

data.append(
{
"tool_name": tool_name or agent_action,
"tool_input": tool_input,
"tool_output": tool_output or agent_output,
"agent_thought": agent_thought,
}
)
return data


def generate_html_1(issues_data, output_file):
html_content = '''
html_content = """
<html>
<head>
<title>Agent Action Report by Issue</title>
Expand All @@ -44,40 +47,40 @@ def generate_html_1(issues_data, output_file):
</head>
<body>
<h1>Agent Action Report by Issue</h1>
'''
"""
for issue, data in issues_data.items():
html_content += f'<h2>{issue}</h2>'
html_content += '''
html_content += f"<h2>{issue}</h2>"
html_content += """
<table>
<tr>
<th>Tool Name</th>
<th>Tool Input</th>
<th>Tool Output</th>
<th>Agent Thought</th>
</tr>
'''
"""
for entry in data:
html_content += f'''
html_content += f"""
<tr>
<td>{entry['tool_name']}</td>
<td>{entry['tool_input']}</td>
<td>{entry['tool_output']}</td>
<td>{entry['agent_thought']}</td>
</tr>
'''
html_content += '</table>'
html_content += '''
"""
html_content += "</table>"
html_content += """
</body>
</html>
'''
"""

with open(output_file, 'w') as file:
with open(output_file, "w") as file:
file.write(html_content)
print(f"HTML report generated: {output_file}")


def generate_html1(issues_data, output_file):
html_content = '''
html_content = """
<html>
<head>
<title>Agent Action Report by Issue</title>
Expand Down Expand Up @@ -107,9 +110,9 @@ def generate_html1(issues_data, output_file):
</head>
<body>
<h1>Agent Action Report by Issue</h1>
'''
"""
for issue_idx, (issue, data) in enumerate(issues_data.items()):
html_content += f'''
html_content += f"""
<button class="collapsible">Issue {issue}</button>
<div class="content">
<table>
Expand All @@ -119,10 +122,10 @@ def generate_html1(issues_data, output_file):
<th>Tool Output</th>
<th>Agent Thought</th>
</tr>
'''
"""
for entry_idx, entry in enumerate(data):
tool_output_id = f"tool-output-content-{issue_idx}-{entry_idx}"
html_content += f'''
html_content += f"""
<tr>
<td>{entry['tool_name']}</td>
<td>{entry['tool_input']}</td>
Expand All @@ -132,10 +135,10 @@ def generate_html1(issues_data, output_file):
</td>
<td>{entry['agent_thought']}</td>
</tr>
'''
html_content += '</table></div>'
"""
html_content += "</table></div>"

html_content += '''
html_content += """
<script>
var coll = document.getElementsByClassName("collapsible");
for (var i = 0; i < coll.length; i++) {
Expand All @@ -152,15 +155,13 @@ def generate_html1(issues_data, output_file):
</script>
</body>
</html>
'''
"""

with open(output_file, 'w') as file:
with open(output_file, "w") as file:
file.write(html_content)
print(f"HTML report generated: {output_file}")




def format_thought(thought):
# print(thought)
# Initial setup for default values if any of the parts are missing
Expand Down Expand Up @@ -189,9 +190,8 @@ def format_thought(thought):
return f'<div class="action">Action: {action}</div><div class="action-input">Action Input: {action_input}</div><div class="thought">Thought: {thought_text}</div>'



def generate_html(issues_data, output_file):
html_content = '''
html_content = """
<html>
<head>
<title>Agent Action Report by Issue</title>
Expand Down Expand Up @@ -219,18 +219,22 @@ def generate_html(issues_data, output_file):
</head>
<body>
<h1>Agent Action Report by Issue</h1>
'''
"""
for issue_idx, (issue, data) in enumerate(issues_data.items()):
html_content += f'<h2>{issue}</h2><table><tr><th>Tool Name</th><th>Tool Input</th><th>Tool Output</th><th>Agent Thought</th></tr>'
html_content += f"<h2>{issue}</h2><table><tr><th>Tool Name</th><th>Tool Input</th><th>Tool Output</th><th>Agent Thought</th></tr>"
for entry_idx, entry in enumerate(data):
content_id = f"content-{issue_idx}-{entry_idx}"
short_output = (entry['tool_output'][:75] + '...') if len(entry['tool_output']) > 78 else entry['tool_output']
formatted_thought = format_thought(entry['agent_thought'])
short_output = (
(entry["tool_output"][:75] + "...")
if len(entry["tool_output"]) > 78
else entry["tool_output"]
)
formatted_thought = format_thought(entry["agent_thought"])
html_content += f'<tr><td>{entry["tool_name"]}</td><td>{entry["tool_input"]}</td><td><span class="collapsible" onclick="toggleVisibility(\'{content_id}\')">{short_output}</span><div id="{content_id}" class="content">{entry["tool_output"]}</div></td><td>{formatted_thought}</td></tr>'
html_content += '</table>'
html_content += '</body></html>'
html_content += "</table>"
html_content += "</body></html>"

with open(output_file, 'w') as file:
with open(output_file, "w") as file:
file.write(html_content)
print(f"HTML report generated: {output_file}")

Expand All @@ -242,6 +246,7 @@ def main(log_file_path, output_html_file):


if __name__ == "__main__":
log_file_path = "</path/to/agent_logs.json>"

log_file_path = sys.argv[1]
output_html_file = "agent_action_report_by_issue.html" # Desired output HTML file
main(log_file_path, output_html_file)

0 comments on commit 7f80125

Please sign in to comment.