This commit is contained in:
Adam Cohen Hillel 2025-04-09 00:37:06 +01:00
parent 17536b9714
commit 6be80eb7ff
3 changed files with 144 additions and 40 deletions

View File

@ -3,8 +3,9 @@ import json
import uuid
from agentpress.thread_manager import ThreadManager
from agent.tools.files_tool import FilesTool
from agent.tools.sb_browse_tool import SandboxTool
from agent.tools.terminal_tool import TerminalTool
from agent.tools.sb_browse_tool import SandboxBrowseTool
from agent.tools.sb_shell_tool import SandboxShellTool
from agent.tools.sb_website_tool import SandboxWebsiteTool
# from agent.tools.search_tool import CodeSearchTool
from typing import Optional
from agent.prompt import get_system_prompt
@ -22,16 +23,19 @@ async def run_agent(thread_id: str, stream: bool = True, thread_manager: Optiona
thread_manager = ThreadManager()
if True: # todo: change to of not sandbox running
sandbox = create_sandbox(TEMP_PASSWORD)
sandbox = create_sandbox("vvv")
sandbox_id = sandbox.id
sandbox_password = "vvv"
else:
sandbox_id = "sandbox-01efaaa5"
sandbox_password = "vvv"
print("Adding tools to thread manager...")
# thread_manager.add_tool(FilesTool)
# thread_manager.add_tool(TerminalTool)
# thread_manager.add_tool(CodeSearchTool)
thread_manager.add_tool(SandboxTool, sandbox_id=sandbox_id, password=sandbox_password)
# thread_manager.add_tool(SandboxBrowseTool, sandbox_id=sandbox_id, password=sandbox_password)
thread_manager.add_tool(SandboxWebsiteTool, sandbox_id=sandbox_id, password=sandbox_password)
system_message = {
"role": "system",
"content": get_system_prompt()

View File

@ -1,56 +1,96 @@
from daytona_sdk.process import SessionExecuteRequest
from agentpress.tool import ToolResult, openapi_schema, xml_schema
from agent.tools.utils.daytona_sandbox import SandboxToolsBase
class SandboxWebsiteTool(SandboxToolsBase):
"""Tool for executing tasks in a Daytona sandbox with browser-use capabilities."""
def __init__(self, sandbox_id: str, password: str):
super().__init__(sandbox_id, password)
@openapi_schema({
"type": "function",
"function": {
"name": "execute_command",
"description": "Execute a shell command in the workspace directory",
"name": "update_website_file",
"description": "Upload or update a file in the website server's site directory",
"parameters": {
"type": "object",
"properties": {
"command": {
"file_path": {
"type": "string",
"description": "The shell command to execute"
"description": "Path where the file should be saved, relative to the site directory"
},
"content": {
"type": "string",
"description": "Content to write to the file"
},
"create_dirs": {
"type": "boolean",
"description": "Create parent directories if they don't exist",
"default": True
}
},
"required": ["command"]
"required": ["file_path", "content"]
}
}
})
@xml_schema(
tag_name="execute-command",
tag_name="update-website-file",
mappings=[
{"param_name": "command", "node_type": "content", "path": "."},
{"param_name": "file_path", "node_type": "attribute", "path": "@path"},
{"param_name": "content", "node_type": "content", "path": "."},
{"param_name": "create_dirs", "node_type": "attribute", "path": "@create_dirs"}
],
example='''
<execute-command>
npm install package-name
</execute-command>
<update-website-file path="index.html" create_dirs="true">
<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
</update-website-file>
'''
)
async def execute_command(self, command: str, folder: str = None) -> ToolResult:
async def update_website_file(self, file_path: str, content: str, create_dirs: bool = True) -> ToolResult:
print(f"\033[33mUpdating website file: {file_path}\033[0m")
try:
folder = folder or self.sandbox.get_user_root_dir()
response = self.sandbox.process.exec(command, cwd=folder, timeout=60)
if response.exit_code == 0:
return self.success_response({
"output": response.result,
"error": "",
"exit_code": response.exit_code,
"cwd": folder
})
else:
return self.fail_response(f"Command failed with exit code {response.exit_code}: {response.result}")
site_dir = "/workspace/site"
full_path = f"{site_dir}/{file_path}"
# Create the site directory if it doesn't exist
self.sandbox.fs.create_folder(site_dir, "755")
# Create parent directories if needed
if create_dirs and '/' in file_path:
parent_dir = '/'.join(file_path.split('/')[:-1])
if parent_dir:
parent_path = f"{site_dir}/{parent_dir}"
self.sandbox.fs.create_folder(parent_path, "755")
# Write the file content using the SDK
self.sandbox.fs.upload_file(full_path, content.encode())
# Set appropriate permissions for web serving
self.sandbox.fs.set_file_permissions(full_path, "644")
# Kill and restart the website server session
self.sandbox.process.delete_session('sandbox_website_server')
self.sandbox.process.create_session('sandbox_website_server')
self.sandbox.process.execute_session_command('sandbox_website_server', SessionExecuteRequest(
command="python " + self.sandbox.get_user_root_dir() + "/website_server.py",
var_async=True
))
return self.success_response({
"message": f"File updated successfully at {file_path}",
"path": file_path,
"site_url": self.sandbox.get_preview_link(8080)
})
except Exception as e:
return self.fail_response(f"Error executing command: {str(e)}")
return self.fail_response(f"Error updating website file: {str(e)}")

View File

@ -3,10 +3,13 @@ import requests
from time import sleep
from daytona_sdk import Daytona, DaytonaConfig, CreateSandboxParams, SessionExecuteRequest
from dotenv import load_dotenv
from agentpress.tool import Tool
from utils.logger import logger
load_dotenv()
config = DaytonaConfig(
api_key=os.getenv("DAYTONA_API_KEY"),
server_url=os.getenv("DAYTONA_SERVER_URL"),
@ -15,7 +18,7 @@ config = DaytonaConfig(
daytona = Daytona(config)
sandbox_api = b'''
sandbox_browser_api = b'''
import traceback
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
@ -145,6 +148,57 @@ if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
'''
sandbox_website_server = b'''
import os
from fastapi import FastAPI, HTTPException, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import uvicorn
import logging
import logging.handlers
# Configure logging
log_dir = "/var/log/kortix"
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, "website_server.log")
logger = logging.getLogger("website_server")
logger.setLevel(logging.INFO)
# Create rotating file handler
file_handler = logging.handlers.RotatingFileHandler(
log_file,
maxBytes=10485760, # 10MB
backupCount=5
)
file_handler.setFormatter(
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
)
logger.addHandler(file_handler)
app = FastAPI()
# Create site directory if it doesn't exist
site_dir = "/workspace/site"
os.makedirs(site_dir, exist_ok=True)
# Mount the static files directory
app.mount("/", StaticFiles(directory=site_dir, html=True), name="site")
@app.get("/health")
async def health_check():
status = {
"status": "healthy"
}
logger.debug(f"Health check: {status}")
return status
if __name__ == "__main__":
logger.info("Starting website server")
uvicorn.run(app, host="0.0.0.0", port=8080)
'''
def create_sandbox(password: str):
sandbox = daytona.create(CreateSandboxParams(
image="adamcohenhillel/kortix-browser-use:0.0.1",
@ -182,13 +236,18 @@ def create_sandbox(password: str):
8080 # HTTP website port
]
))
sandbox.fs.upload_file(sandbox.get_user_root_dir() + "/app.py", sandbox_api)
sandbox.process.create_session('kortix_browser_use_api')
rsp = sandbox.process.execute_session_command('kortix_browser_use_api', SessionExecuteRequest(
command="python " + sandbox.get_user_root_dir() + "/app.py",
sandbox.fs.upload_file(sandbox.get_user_root_dir() + "/browser_api.py", sandbox_browser_api)
sandbox.fs.upload_file(sandbox.get_user_root_dir() + "/website_server.py", sandbox_website_server)
sandbox.process.create_session('sandbox_browser_api')
sandbox.process.create_session('sandbox_website_server')
rsp = sandbox.process.execute_session_command('sandbox_browser_api', SessionExecuteRequest(
command="python " + sandbox.get_user_root_dir() + "/browser_api.py",
var_async=True
))
rsp2 = sandbox.process.execute_session_command('sandbox_website_server', SessionExecuteRequest(
command="python " + sandbox.get_user_root_dir() + "/website_server.py",
var_async=True
))
times = 0
success = False
api_url = sandbox.get_preview_link(8000)
@ -208,6 +267,7 @@ def create_sandbox(password: str):
raise Exception("API call failed")
logger.info(f"Executed command {rsp}")
logger.info(f"Executed command {rsp2}")
logger.info(f"Created kortix_browser_use_api session `kortix_browser_use_api`")
return sandbox
@ -231,5 +291,5 @@ class SandboxToolsBase(Tool):
print("\033[95m***")
print(self.sandbox.get_preview_link(6080))
print("***\033[0m")
print(self.sandbox.get_preview_link(8080))
print("***\033[0m")