suna/tools/files_tool.py

167 lines
6.6 KiB
Python
Raw Normal View History

2024-10-06 01:04:15 +08:00
import os
import asyncio
2024-10-28 05:04:42 +08:00
from datetime import datetime
2024-10-23 09:42:38 +08:00
from agentpress.tool import Tool, ToolResult, tool_schema
2024-10-28 05:04:42 +08:00
from state_manager import StateManager
2024-10-06 01:04:15 +08:00
class FilesTool(Tool):
def __init__(self):
super().__init__()
2024-10-23 10:23:50 +08:00
self.workspace = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'workspace')
2024-10-06 01:04:15 +08:00
os.makedirs(self.workspace, exist_ok=True)
2024-10-28 05:04:42 +08:00
self.state_manager = StateManager("state.json")
asyncio.create_task(self._init_workspace_state())
async def _init_workspace_state(self):
"""Initialize or update the workspace state in JSON"""
files_state = {}
# Walk through workspace and record all files
for root, _, files in os.walk(self.workspace):
for file in files:
full_path = os.path.join(root, file)
# Get path relative to workspace: e.g. "src/components/file.jsx"
rel_path = os.path.relpath(full_path, self.workspace)
try:
with open(full_path, 'r') as f:
content = f.read()
files_state[rel_path] = content
except Exception as e:
print(f"Error reading file {rel_path}: {e}")
await self.state_manager.set("files", files_state)
async def _update_workspace_state(self):
"""Update the workspace state after any file operation"""
await self._init_workspace_state()
2024-10-06 01:04:15 +08:00
2024-10-23 09:42:38 +08:00
@tool_schema({
"name": "create_file",
"description": "Create a new file in the workspace",
"parameters": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "The relative path of the file to create"},
"content": {"type": "string", "description": "The content to write to the file"}
},
"required": ["file_path", "content"]
}
})
2024-10-06 01:04:15 +08:00
async def create_file(self, file_path: str, content: str) -> ToolResult:
try:
full_path = os.path.join(self.workspace, file_path)
if os.path.exists(full_path):
return self.fail_response(f"File '{file_path}' already exists. Use update_file to modify existing files.")
2024-10-28 05:04:42 +08:00
2024-10-06 01:04:15 +08:00
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(full_path, 'w') as f:
f.write(content)
2024-10-28 05:04:42 +08:00
await self._update_workspace_state()
2024-10-06 01:04:15 +08:00
return self.success_response(f"File '{file_path}' created successfully.")
except Exception as e:
return self.fail_response(f"Error creating file: {str(e)}")
2024-10-23 09:42:38 +08:00
@tool_schema({
"name": "read_file",
"description": "Read the contents of a file in the workspace",
"parameters": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "The relative path of the file to read"}
},
"required": ["file_path"]
}
})
2024-10-06 01:04:15 +08:00
async def read_file(self, file_path: str) -> ToolResult:
try:
2024-10-28 05:04:42 +08:00
workspace_state = await self.state_manager.get("workspace")
if file_path in workspace_state["files"]:
return self.success_response({
"file_path": file_path,
"content": workspace_state["files"][file_path]["content"]
})
return self.fail_response(f"File '{file_path}' not found in workspace state.")
2024-10-06 01:04:15 +08:00
except Exception as e:
return self.fail_response(f"Error reading file: {str(e)}")
2024-10-23 09:42:38 +08:00
@tool_schema({
"name": "update_file",
"description": "Update the contents of a file in the workspace",
"parameters": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "The relative path of the file to update"},
"content": {"type": "string", "description": "The new content to write to the file"}
},
"required": ["file_path", "content"]
}
})
2024-10-06 01:04:15 +08:00
async def update_file(self, file_path: str, content: str) -> ToolResult:
try:
full_path = os.path.join(self.workspace, file_path)
with open(full_path, 'w') as f:
f.write(content)
2024-10-28 05:04:42 +08:00
await self._update_workspace_state()
2024-10-06 01:04:15 +08:00
return self.success_response(f"File '{file_path}' updated successfully.")
except Exception as e:
return self.fail_response(f"Error updating file: {str(e)}")
2024-10-23 09:42:38 +08:00
@tool_schema({
"name": "delete_file",
"description": "Delete a file from the workspace",
"parameters": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "The relative path of the file to delete"}
},
"required": ["file_path"]
}
})
2024-10-06 01:04:15 +08:00
async def delete_file(self, file_path: str) -> ToolResult:
try:
full_path = os.path.join(self.workspace, file_path)
os.remove(full_path)
2024-10-28 05:04:42 +08:00
await self._update_workspace_state()
2024-10-06 01:04:15 +08:00
return self.success_response(f"File '{file_path}' deleted successfully.")
except Exception as e:
return self.fail_response(f"Error deleting file: {str(e)}")
if __name__ == "__main__":
async def test_files_tool():
files_tool = FilesTool()
test_file_path = "test_file.txt"
test_content = "This is a test file."
updated_content = "This is an updated test file."
print(f"Using workspace directory: {files_tool.workspace}")
# Test create_file
create_result = await files_tool.create_file(test_file_path, test_content)
print("Create file result:", create_result)
# Test read_file
read_result = await files_tool.read_file(test_file_path)
print("Read file result:", read_result)
# Test update_file
update_result = await files_tool.update_file(test_file_path, updated_content)
print("Update file result:", update_result)
# Test read_file after update
read_updated_result = await files_tool.read_file(test_file_path)
print("Read updated file result:", read_updated_result)
# Test delete_file
delete_result = await files_tool.delete_file(test_file_path)
print("Delete file result:", delete_result)
# Test read_file after delete (should fail)
read_deleted_result = await files_tool.read_file(test_file_path)
print("Read deleted file result:", read_deleted_result)
2024-10-23 09:42:38 +08:00
asyncio.run(test_files_tool())