support use existing sandbox by id
This commit is contained in:
@@ -17,7 +17,8 @@ from app.tool.python_execute import PythonExecute
|
||||
from app.tool.str_replace_editor import StrReplaceEditor
|
||||
|
||||
from app.tool.sb_browser_tool import SandboxBrowserTool
|
||||
from app.daytona.sandbox import create_sandbox
|
||||
from app.daytona.sandbox import create_sandbox,get_or_start_sandbox
|
||||
import daytona
|
||||
|
||||
class SandboxManus(ToolCallAgent):
|
||||
"""A versatile general-purpose agent with support for both local and MCP tools."""
|
||||
@@ -53,6 +54,7 @@ class SandboxManus(ToolCallAgent):
|
||||
default_factory=dict
|
||||
) # server_id -> url/command
|
||||
_initialized: bool = False
|
||||
sandbox_link: Optional[dict[str, dict[str, str]]] = Field(default_factory=dict)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def initialize_helper(self) -> "SandboxManus":
|
||||
@@ -65,16 +67,67 @@ class SandboxManus(ToolCallAgent):
|
||||
"""Factory method to create and properly initialize a Manus instance."""
|
||||
instance = cls(**kwargs)
|
||||
await instance.initialize_mcp_servers()
|
||||
instance.initialize_sandbox_tools()
|
||||
await instance.initialize_sandbox_tools()
|
||||
instance._initialized = True
|
||||
return instance
|
||||
|
||||
def initialize_sandbox_tools(self,password="123456") -> None:
|
||||
sandbox = create_sandbox(password=password)
|
||||
print(f"VNC link: {sandbox.get_preview_link(6080)}")
|
||||
computer_tool = SandboxBrowserTool.create_with_sandbox(sandbox)
|
||||
sandbox_tools=[computer_tool]
|
||||
self.available_tools.add_tools(*sandbox_tools)
|
||||
async def initialize_sandbox_tools(self, sandbox_id: str = config.daytona.sandbox_id, password:str = config.daytona.VNC_password) -> None:
|
||||
try:
|
||||
if sandbox_id:
|
||||
sandbox = await get_or_start_sandbox(sandbox_id)
|
||||
|
||||
# Initialize sandbox_link if not exists
|
||||
if not self.sandbox_link:
|
||||
self.sandbox_link = {}
|
||||
|
||||
# Check if URLs are already cached
|
||||
if sandbox_id in self.sandbox_link:
|
||||
vnc_url = self.sandbox_link[sandbox_id]["vnc"]
|
||||
website_url = self.sandbox_link[sandbox_id]["website"]
|
||||
logger.info(f"Using cached URLs - VNC: {vnc_url}, Website: {website_url}")
|
||||
else:
|
||||
# Get URLs from sandbox and cache them
|
||||
vnc_link = sandbox.get_preview_link(6080)
|
||||
website_link = sandbox.get_preview_link(8080)
|
||||
vnc_url = vnc_link.url if hasattr(vnc_link, 'url') else str(vnc_link)
|
||||
website_url = website_link.url if hasattr(website_link, 'url') else str(website_link)
|
||||
|
||||
# Cache the URLs
|
||||
self.sandbox_link[sandbox_id] = {
|
||||
"vnc": str(vnc_url),
|
||||
"website": str(website_url)
|
||||
}
|
||||
logger.info(f"Cached new URLs - VNC: {vnc_url}, Website: {website_url}")
|
||||
|
||||
logger.info(f"VNC URL: {vnc_url}")
|
||||
logger.info(f"Website URL: {website_url}")
|
||||
else:
|
||||
# 创建新沙箱
|
||||
if password:
|
||||
sandbox = create_sandbox(password=password)
|
||||
else:
|
||||
raise ValueError("Sandbox ID or password must be provided")
|
||||
vnc_link = sandbox.get_preview_link(6080)
|
||||
website_link = sandbox.get_preview_link(8080)
|
||||
vnc_url = vnc_link.url if hasattr(vnc_link, 'url') else str(vnc_link)
|
||||
website_url = website_link.url if hasattr(website_link, 'url') else str(website_link)
|
||||
|
||||
# Get the actual sandbox_id from the created sandbox
|
||||
actual_sandbox_id = sandbox.id if hasattr(sandbox, 'id') else 'new_sandbox'
|
||||
if not self.sandbox_link:
|
||||
self.sandbox_link = {}
|
||||
self.sandbox_link[actual_sandbox_id] = {
|
||||
"vnc": vnc_url,
|
||||
"website": website_url,
|
||||
}
|
||||
logger.info(f"VNC URL: {vnc_url}")
|
||||
logger.info(f"Website URL: {website_url}")
|
||||
computer_tool = SandboxBrowserTool.create_with_sandbox(sandbox)
|
||||
self.available_tools.add_tools(computer_tool)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error initializing sandbox tools: {e}")
|
||||
raise
|
||||
|
||||
async def initialize_mcp_servers(self) -> None:
|
||||
"""Initialize connections to configured MCP servers."""
|
||||
|
||||
@@ -111,6 +111,8 @@ class DaytonaSettings(BaseModel):
|
||||
daytona_target: Optional[str] = Field("us", description="enum ['asia', 'eu', 'us']")
|
||||
sandbox_image_name: Optional[str]= Field("kortix/suna:0.1.3", description="")
|
||||
sandbox_entrypoint: Optional[str]= Field("/usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf", description="")
|
||||
sandbox_id: Optional[str] = Field(None, description="ID of the daytona sandbox to use, if any")
|
||||
VNC_password: Optional[str] = Field("123456", description="VNC password for the vnc service in sandbox")
|
||||
|
||||
class MCPServerConfig(BaseModel):
|
||||
"""Configuration for a single MCP server"""
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# Global LLM configuration
|
||||
[llm]
|
||||
model = "claude-3-7-sonnet-20250219" # The LLM model to use
|
||||
base_url = "https://api.anthropic.com/v1/" # API endpoint URL
|
||||
api_key = "YOUR_API_KEY" # Your API key
|
||||
max_tokens = 8192 # Maximum number of tokens in the response
|
||||
temperature = 0.0 # Controls randomness
|
||||
|
||||
# [llm] # Amazon Bedrock
|
||||
# api_type = "aws" # Required
|
||||
# model = "us.anthropic.claude-3-7-sonnet-20250219-v1:0" # Bedrock supported modelID
|
||||
# base_url = "bedrock-runtime.us-west-2.amazonaws.com" # Not used now
|
||||
# max_tokens = 8192
|
||||
# temperature = 1.0
|
||||
# api_key = "bear" # Required but not used for Bedrock
|
||||
|
||||
# [llm] #AZURE OPENAI:
|
||||
# api_type= 'azure'
|
||||
# model = "YOUR_MODEL_NAME" #"gpt-4o-mini"
|
||||
# base_url = "{YOUR_AZURE_ENDPOINT.rstrip('/')}/openai/deployments/{AZURE_DEPLOYMENT_ID}"
|
||||
# api_key = "AZURE API KEY"
|
||||
# max_tokens = 8096
|
||||
# temperature = 0.0
|
||||
# api_version="AZURE API VERSION" #"2024-08-01-preview"
|
||||
|
||||
# [llm] #OLLAMA:
|
||||
# api_type = 'ollama'
|
||||
# model = "llama3.2"
|
||||
# base_url = "http://localhost:11434/v1"
|
||||
# api_key = "ollama"
|
||||
# max_tokens = 4096
|
||||
# temperature = 0.0
|
||||
|
||||
# Optional configuration for specific LLM models
|
||||
[llm.vision]
|
||||
model = "claude-3-7-sonnet-20250219" # The vision model to use
|
||||
base_url = "https://api.anthropic.com/v1/" # API endpoint URL for vision model
|
||||
api_key = "YOUR_API_KEY" # Your API key for vision model
|
||||
max_tokens = 8192 # Maximum number of tokens in the response
|
||||
temperature = 0.0 # Controls randomness for vision model
|
||||
|
||||
# [llm.vision] #OLLAMA VISION:
|
||||
# api_type = 'ollama'
|
||||
# model = "llama3.2-vision"
|
||||
# base_url = "http://localhost:11434/v1"
|
||||
# api_key = "ollama"
|
||||
# max_tokens = 4096
|
||||
# temperature = 0.0
|
||||
|
||||
# Optional configuration for specific browser configuration
|
||||
# [browser]
|
||||
# Whether to run browser in headless mode (default: false)
|
||||
#headless = false
|
||||
# Disable browser security features (default: true)
|
||||
#disable_security = true
|
||||
# Extra arguments to pass to the browser
|
||||
#extra_chromium_args = []
|
||||
# Path to a Chrome instance to use to connect to your normal browser
|
||||
# e.g. '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
|
||||
#chrome_instance_path = ""
|
||||
# Connect to a browser instance via WebSocket
|
||||
#wss_url = ""
|
||||
# Connect to a browser instance via CDP
|
||||
#cdp_url = ""
|
||||
|
||||
# Optional configuration, Proxy settings for the browser
|
||||
# [browser.proxy]
|
||||
# server = "http://proxy-server:port"
|
||||
# username = "proxy-username"
|
||||
# password = "proxy-password"
|
||||
|
||||
# Optional configuration, Search settings.
|
||||
# [search]
|
||||
# Search engine for agent to use. Default is "Google", can be set to "Baidu" or "DuckDuckGo" or "Bing".
|
||||
#engine = "Google"
|
||||
# Fallback engine order. Default is ["DuckDuckGo", "Baidu", "Bing"] - will try in this order after primary engine fails.
|
||||
#fallback_engines = ["DuckDuckGo", "Baidu", "Bing"]
|
||||
# Seconds to wait before retrying all engines again when they all fail due to rate limits. Default is 60.
|
||||
#retry_delay = 60
|
||||
# Maximum number of times to retry all engines when all fail. Default is 3.
|
||||
#max_retries = 3
|
||||
# Language code for search results. Options: "en" (English), "zh" (Chinese), etc.
|
||||
#lang = "en"
|
||||
# Country code for search results. Options: "us" (United States), "cn" (China), etc.
|
||||
#country = "us"
|
||||
|
||||
|
||||
## Sandbox configuration
|
||||
#[sandbox]
|
||||
#use_sandbox = false
|
||||
#image = "python:3.12-slim"
|
||||
#work_dir = "/workspace"
|
||||
#memory_limit = "1g" # 512m
|
||||
#cpu_limit = 2.0
|
||||
#timeout = 300
|
||||
#network_enabled = true
|
||||
|
||||
# Daytona configuration
|
||||
#[daytona]
|
||||
#daytona_api_key = ""
|
||||
#daytona_server_url = "https://app.daytona.io/api"
|
||||
#daytona_target = "us"
|
||||
#sandbox_image_name = "kortix/suna:0.1.3"
|
||||
#sandbox_entrypoint =
|
||||
#sandbox_id =
|
||||
#VNC_password =
|
||||
|
||||
# MCP (Model Context Protocol) configuration
|
||||
[mcp]
|
||||
server_reference = "app.mcp.server" # default server module reference
|
||||
|
||||
# Optional Runflow configuration
|
||||
# Your can add additional agents into run-flow workflow to solve different-type tasks.
|
||||
[runflow]
|
||||
use_data_analysis_agent = false # The Data Analysi Agent to solve various data analysis tasks
|
||||
@@ -95,14 +95,6 @@ temperature = 0.0 # Controls randomness for vision mode
|
||||
#timeout = 300
|
||||
#network_enabled = true
|
||||
|
||||
# Daytona configuration
|
||||
[daytona]
|
||||
daytona_api_key = ""
|
||||
daytona_server_url = "https://app.daytona.io/api"
|
||||
daytona_target = "us"
|
||||
#sandbox_image_name =
|
||||
#sandbox_entrypoint =
|
||||
|
||||
# MCP (Model Context Protocol) configuration
|
||||
[mcp]
|
||||
server_reference = "app.mcp.server" # default server module reference
|
||||
|
||||
Reference in New Issue
Block a user