diff --git a/agents/agentscope/pom.xml b/agents/agentscope/pom.xml index 4981fa26..1081fd72 100644 --- a/agents/agentscope/pom.xml +++ b/agents/agentscope/pom.xml @@ -46,7 +46,7 @@ io.agentscope - agentscope-runtime-core + agentscope-runtime-engine 1.0.1 diff --git a/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/ToolkitInit.java b/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/ToolkitInit.java index cf79c9ba..1bb462e3 100644 --- a/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/ToolkitInit.java +++ b/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/ToolkitInit.java @@ -23,8 +23,7 @@ import io.agentscope.runtime.engine.agents.agentscope.tools.fs.*; import io.agentscope.runtime.engine.agents.agentscope.tools.mcp.AsMCPTool; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.tools.MCPTool; import io.agentscope.runtime.sandbox.tools.McpConfigConverter; import org.slf4j.Logger; @@ -299,26 +298,26 @@ public static AgentTool SearchFilesTool(Sandbox sandbox) { } public static List getMcpTools(String serverConfigs, - SandboxType sandboxType, - SandboxManager sandboxManager) { - return getMcpTools(serverConfigs, sandboxType, sandboxManager, null, null); + String sandboxType, + SandboxService sandboxService) { + return getMcpTools(serverConfigs, sandboxType, sandboxService, null, null); } public static List getMcpTools(Map serverConfigs, - SandboxType sandboxType, - SandboxManager sandboxManager) { - return getMcpTools(serverConfigs, sandboxType, sandboxManager, null, null); + String sandboxType, + SandboxService sandboxService) { + return getMcpTools(serverConfigs, sandboxType, sandboxService, null, null); } public static List getMcpTools(String serverConfigs, - SandboxType sandboxType, - SandboxManager sandboxManager, + String sandboxType, + SandboxService sandboxService, Set whitelist, Set blacklist) { McpConfigConverter converter = McpConfigConverter.builder() .serverConfigs(serverConfigs) .sandboxType(sandboxType) - .sandboxManager(sandboxManager) + .sandboxService(sandboxService) .whitelist(whitelist) .blacklist(blacklist) .build(); @@ -327,14 +326,14 @@ public static List getMcpTools(String serverConfigs, } public static List getMcpTools(Map serverConfigs, - SandboxType sandboxType, - SandboxManager sandboxManager, + String sandboxType, + SandboxService sandboxService, Set whitelist, Set blacklist) { McpConfigConverter converter = McpConfigConverter.builder() .serverConfigs(serverConfigs) .sandboxType(sandboxType) - .sandboxManager(sandboxManager) + .sandboxService(sandboxService) .whitelist(whitelist) .blacklist(blacklist) .build(); @@ -343,23 +342,23 @@ public static List getMcpTools(Map serverConfigs, } public static List getMcpTools(String serverConfigs, - SandboxManager sandboxManager) { - return getMcpTools(serverConfigs, null, sandboxManager, null, null); + SandboxService sandboxService) { + return getMcpTools(serverConfigs, null, sandboxService, null, null); } public static List getMcpTools(Map serverConfigs, - SandboxManager sandboxManager) { - return getMcpTools(serverConfigs, null, sandboxManager, null, null); + SandboxService sandboxService) { + return getMcpTools(serverConfigs, null, sandboxService, null, null); } // public static List getAllToolsWithMcp(String mcpServerConfigs, -// SandboxType sandboxType, -// SandboxManager sandboxManager) { +// String sandboxType, +// SandboxService sandboxService) { // List allTools = new ArrayList<>(getAllTools()); // // if (mcpServerConfigs != null && !mcpServerConfigs.trim().isEmpty()) { // try { -// List mcpTools = getMcpTools(mcpServerConfigs, sandboxType, sandboxManager); +// List mcpTools = getMcpTools(mcpServerConfigs, sandboxType, sandboxService); // allTools.addAll(mcpTools); // logger.info(String.format("Added %d MCP tools to the toolkit", mcpTools.size())); // } catch (Exception e) { @@ -371,13 +370,13 @@ public static List getMcpTools(Map serverConfigs, // } // public static List getAllToolsWithMcp(Map mcpServerConfigs, -// SandboxType sandboxType, -// SandboxManager sandboxManager) { +// String sandboxType, +// SandboxService sandboxService) { // List allTools = new ArrayList<>(getAllTools()); // // if (mcpServerConfigs != null && !mcpServerConfigs.isEmpty()) { // try { -// List mcpTools = getMcpTools(mcpServerConfigs, sandboxType, sandboxManager); +// List mcpTools = getMcpTools(mcpServerConfigs, sandboxType, sandboxService); // allTools.addAll(mcpTools); // logger.info(String.format("Added %d MCP tools to the toolkit", mcpTools.size())); // } catch (Exception e) { @@ -389,24 +388,24 @@ public static List getMcpTools(Map serverConfigs, // } public static List createMcpToolInstances(String serverConfigs, - SandboxType sandboxType, - SandboxManager sandboxManager) { + String sandboxType, + SandboxService sandboxService) { McpConfigConverter converter = McpConfigConverter.builder() .serverConfigs(serverConfigs) .sandboxType(sandboxType) - .sandboxManager(sandboxManager) + .sandboxService(sandboxService) .build(); return converter.toBuiltinTools(); } public static List createMcpToolInstances(Map serverConfigs, - SandboxType sandboxType, - SandboxManager sandboxManager) { + String sandboxType, + SandboxService sandboxService) { McpConfigConverter converter = McpConfigConverter.builder() .serverConfigs(serverConfigs) .sandboxType(sandboxType) - .sandboxManager(sandboxManager) + .sandboxService(sandboxService) .build(); return converter.toBuiltinTools(); diff --git a/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/mcp/AsMCPTool.java b/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/mcp/AsMCPTool.java index e24b7a4e..f659455d 100644 --- a/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/mcp/AsMCPTool.java +++ b/agents/agentscope/src/main/java/io/agentscope/runtime/engine/agents/agentscope/tools/mcp/AsMCPTool.java @@ -18,8 +18,7 @@ import io.agentscope.core.message.ToolResultBlock; import io.agentscope.runtime.engine.agents.agentscope.tools.AgentScopeSandboxAwareTool; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.tools.MCPTool; import reactor.core.publisher.Mono; @@ -29,14 +28,14 @@ public class AsMCPTool extends AgentScopeSandboxAwareTool { public AsMCPTool(String name, String toolType, String description, Map schema, Map serverConfigs, - SandboxType sandboxType, SandboxManager sandboxManager) { + String sandboxType, SandboxService sandboxService) { super(new MCPTool(name, toolType, description, schema, serverConfigs, sandboxType, - sandboxManager)); + sandboxService)); } public AsMCPTool(MCPTool mcpTool) { diff --git a/cookbook/en/deployment/agent_app.md b/cookbook/en/deployment/agent_app.md index 5e48ab7e..ef4c978a 100644 --- a/cookbook/en/deployment/agent_app.md +++ b/cookbook/en/deployment/agent_app.md @@ -351,10 +351,9 @@ import io.agentscope.runtime.app.AgentApp; import io.agentscope.runtime.engine.services.agent_state.InMemoryStateService; import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.jetbrains.annotations.NotNull; /** @@ -364,7 +363,7 @@ import org.jetbrains.annotations.NotNull; *
    *
  • Session state management (in-memory storage)
  • *
  • Short-term memory (session history) and long-term memory (user-level memory store)
  • - *
  • Python sandbox tool support (isolated execution environment via Kubernetes)
  • + *
  • Python sandbox tool support (isolated execution environment via Docker)
  • *
  • Streaming agent inference service via HTTP interface
  • *
* @@ -403,19 +402,18 @@ public class AgentScopeDeployExample { } /** - * Build sandbox service instance, using Kubernetes client configuration by default. + * Build sandbox service instance, using Docker client configuration by default. * * @return Configured SandboxService instance */ @NotNull private static SandboxService buildSandboxService() { - // Use default Kubernetes configuration (can be replaced with actual cluster configuration for deployment) - var clientConfig = KubernetesClientConfig.builder().build(); + var clientConfig = DockerClientStarter.builder().build(); var managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) - .build(); + .clientStarter(clientConfig) + .build(); - return new SandboxService(new SandboxManager(managerConfig)); + return new SandboxService(managerConfig); } } ``` diff --git a/cookbook/en/sandbox/advanced.md b/cookbook/en/sandbox/advanced.md index 67a3154e..a39eb6d0 100644 --- a/cookbook/en/sandbox/advanced.md +++ b/cookbook/en/sandbox/advanced.md @@ -6,39 +6,32 @@ #### ManagerConfig Configuration -| Parameter | Type | Description | Default | Notes | -| ----------------------| ------------ | ---------------------- | -------------------------- | ------------------------------------------------------------ | -| `defaultSandboxType` | `List` | Default sandbox type(s) (can be multiple) | `SandboxType.BASE` | Can be a single type or a list of multiple types, enabling multiple independent sandbox warm-up pools. Valid values include `BASE`, `BROWSER`, `FILESYSTEM`, `GUI`, etc. | -| `bearerToken` | `String` | Authentication token for calling remote runtime sandbox | `null` | If set to `null`, no authentication will be performed when connecting | -| `baseUrl` | `String` | Server binding address for calling remote runtime sandbox | `null` | If set to `null`, local sandbox management will be used by default | -| `containerDeployment` | `BaseClientConfig` | Container runtime | `DockerClientConfig` | Currently supports `Docker`, `K8s`, and `AgentRun` | -| `poolSize` | `int` | Warm-up container pool size | `0` | Cached containers for faster startup. The `poolSize` parameter controls the number of pre-created containers cached in ready state. When a user requests a new sandbox, the system will first try to allocate from this warm-up pool, significantly reducing startup time compared to creating containers from scratch. For example, with `poolSize=10`, the system maintains 10 ready containers that can be immediately assigned to new requests | -| `fileSystemConfig` | `FileSystemConfig` | Container file system configuration | `LocalFileSystemConfig` | Manages container file system download method, defaults to `local file system`, can also use `oss` | -| `redisConfig` | `RedisManagerConfig` | Redis support configuration | `null` | Enable Redis support, required for distributed deployment or when number of worker processes is greater than `1`, disabled by default | +| Parameter | Type | Description | Default | Notes | +| ----------------------| ------------ |-----------------------------------------------------------| -------------------------- |---------------------------------------------------------------------------------------------------------------------------------------------------| +| `bearerToken` | `String` | Authentication token for calling remote runtime sandbox | `null` | If set to `null`, no authentication will be performed when connecting | +| `baseUrl` | `String` | Server binding address for calling remote runtime sandbox | `null` | If set to `null`, local sandbox management will be used by default | +| `clientStarter` | `BaseClientStarter` | Container runtime | `DockerClientStarter` | Currently supports `Docker`, `K8s`, `AgentRun` and `FC` | +| `sandboxMap` | `SandboxMap` | Container Management | `InMemorySandboxMap` | Container management uses local storage by default. Introducing the `redis-extension` allows using `redis` as the management backend. | +| `containerPrefixKey` | `String` | Container name prefix | `sandbox_container_` | Prefix for created container names | + #### Redis Configuration -> **When to use Redis:** -> - **Single worker process (`WORKERS=1`)**: Redis is optional. The system can use memory cache to manage sandbox state, which is simpler and has lower latency. -> - **Multiple worker processes (`WORKERS>1`)**: Redis is required to share sandbox state between worker processes and ensure consistency. +Currently, mounting file directories is only supported under local Docker, with three types of mounts available: + +* Copy Mount (recommended): Configure the `storageFolderPath` and `mountDir` properties. When creating a container, the contents of the storage directory are first copied to the mount path, and the container mounts this mount path to prevent contamination of the original files. When the container shuts down, the contents of the mount path are copied back. +* Read-only Mount: Configure `readonlyMounts`. The container directly mounts the local file system in read-only mode. +* Read-write Zero-copy Mount (use with caution): Configure `nonCopyMount`. The container directly mounts the local file system and has full read-write permissions to the folder. -Redis provides caching for sandbox state and state management. If there is only one worker process, you can use memory cache: +When using this feature, you need to pass a `FileSystemConfig` instance to the `fileSystemConfig` parameter of the `Sandbox`. -| Parameter | Description | Default | Notes | -| ----------------------- | ---------------- | ------------------------------------------- | ---------------- | -| `redisServer` | Redis server address | localhost | Redis host | -| `redisPort` | Redis port | 6379 | Standard Redis port | -| `redisDb` | Redis database number | `0` | 0-15 | -| `redisUser` | Redis username | `null` | For Redis6+ ACL | -| `redisPassword` | Redis password | `null` | Authentication | -| `redisPortKey` | Port tracking key | `_runtime_sandbox_container_occupied_ports` | Internal use | -| `redisContainerPoolKey` | Container pool key | `_runtime_sandbox_container_container_pool` | Internal use | +##### Storage Backend -#### FileSystemConfig Configuration +###### Local Storage Defaults to `LocalFileSystemConfig`, no configuration needed for local conditions. Configuration is required when using `oss`. -##### OSS Configuration +###### OSS Configuration Use [Alibaba Cloud Object Storage Service](https://www.aliyun.com/product/oss) for distributed file storage: @@ -115,25 +108,47 @@ To configure [FC](https://fcnext.console.aliyun.com/)-specific settings in the s | `FcLogProject` | SLS log project | `null` | SLS log project name (optional) | | `FcLogStore` | SLS log store | `null` | SLS log store name (optional) | -### Importing Custom Sandbox +### Importing Custom Sandboxes + +In addition to the default built-in sandbox types, you can implement custom sandboxes by using the `@RegisterSandbox` annotation and extending the `Sandbox` class. This enables customization such as changing the container image, adding environment variables, defining timeout durations, and more. The application automatically scans all classes annotated with `@RegisterSandbox` at startup and registers them automatically. (Currently, only one custom sandbox is supported; future versions will allow multiple custom sandboxes.) + +#### Implement a Custom Sandbox Extension (e.g., `CustomSandbox.java`) + +Refer to [Custom Sandbox Class](###creating-a-custom-sandbox-class). + +> - The `@RegisterSandbox` annotation registers the class with the sandbox manager, making it recognizable and usable at startup. +> - The `environment` field can inject external API keys or other required configurations into the sandbox. +> - By inheriting from `Sandbox`, you can override its methods to implement additional custom logic. + +#### Add Java SPI Scan Configuration -In addition to the default provided base sandbox types, you can also implement custom sandbox functionality by writing extension modules and loading them with the `--extension` parameter, such as modifying images, adding environment variables, defining timeouts, etc. +AgentScope Runtime for Java provides `SandboxProvider` as the base class for sandbox discovery. You need to create a class in your project that implements `SandboxProvider` and includes your custom sandbox, as shown below: -#### Writing Custom Sandbox Extension (e.g., `CustomSandbox.java`) +```java +import io.agentscope.runtime.sandbox.manager.registry.SandboxProvider; -Refer to [Creating Custom Sandbox Class](#creating-custom-sandbox-class) +import java.util.Collection; +import java.util.Collections; -> - `@RegisterSandbox` will register this class to the sandbox manager and can be recognized and used at startup. -> - The `environment` field can inject external API Keys or other necessary configurations into the sandbox. -> - The class inherits from `Sandbox` and can override its methods to implement more custom logic. +public class CustomSandboxProvider implements SandboxProvider { -## Custom Sandbox Building + @Override + public Collection> getSandboxClasses() { + // Register the custom CustomSandbox + return Collections.singletonList(CustomSandbox.class); + } +} +``` + +Then, in the `resources` directory, create a folder named `META-INF/services`, and inside it, create a file named `io.agentscope.runtime.sandbox.manager.registry.SandboxProvider`. In this file, add the fully qualified class name of your `SandboxProvider` implementation, for example: `io.agentscope.CustomSandboxProvider`. -While built-in sandbox types cover common use cases, you may encounter scenarios that require specialized environments or unique tool combinations. Creating custom sandboxes allows you to tailor the execution environment to specific needs. This section demonstrates how to build and register your custom sandbox type. +## Building Custom Sandboxes -### Install from Source (Required for Custom Sandbox) +Although the built-in sandbox types cover common use cases, you may encounter scenarios that require a specialized environment or a unique combination of tools. Creating a custom sandbox allows you to tailor the execution environment to your specific needs. This section demonstrates how to build and register your own custom sandbox type. -To create custom sandboxes, you need to use the Python version of [AgentScope Runtime](https://github.com/agentscope-ai/agentscope-runtime). Install AgentScope Runtime from source in editable mode, which allows you to modify code and see changes immediately: +### Install from Source (Required for Custom Sandboxes) + +To create a custom sandbox, you need to use the Python version of [AgentScope Runtime](https://github.com/agentscope-ai/agentscope-runtime). Install AgentScope Runtime from source in editable mode, which allows you to modify the code and immediately see the changes: ```bash git clone https://github.com/agentscope-ai/agentscope-runtime.git @@ -143,53 +158,37 @@ pip install -e . ``` > The `-e` (editable) flag is required when creating custom sandboxes because it allows you to: -> - Modify sandbox code and see changes immediately without reinstalling -> - Add your custom sandbox classes to the registry -> - Iterate on development and testing of custom tools - -### Creating Custom Sandbox Class - -You can define custom sandbox types and register them with the system to meet special requirements. Simply inherit from `Sandbox` and use the `SandboxRegistry.register` decorator, then place the file in `src/agentscope_runtime/sandbox/custom` (e.g., `src/agentscope_runtime/sandbox/custom/custom_sandbox.py`): - -```python -import os +> +> - Modify sandbox code and immediately see changes without reinstalling +> - Add your custom sandbox class to the registry +> - Iteratively develop and test custom tools -from typing import Optional +### Create a Custom Sandbox Class -from agentscope_runtime.sandbox.utils import build_image_uri -from agentscope_runtime.sandbox.registry import SandboxRegistry -from agentscope_runtime.sandbox.enums import SandboxType -from agentscope_runtime.sandbox.box.sandbox import Sandbox +You can define a custom sandbox type and register it with the system to meet specific requirements. Simply inherit from `Sandbox` and use the `@RegisterSandbox` annotation, then place the file in your project directory: -SANDBOXTYPE = "my_custom_sandbox" +```java +import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; -@SandboxRegistry.register( - build_image_uri(f"runtime-sandbox-{SANDBOXTYPE}"), - sandbox_type=SANDBOXTYPE, - security_level="medium", - timeout=60, - description="my sandbox", - environment={ - "TAVILY_API_KEY": os.getenv("TAVILY_API_KEY", ""), - "AMAP_MAPS_API_KEY": os.getenv("AMAP_MAPS_API_KEY", ""), - }, +@RegisterSandbox( + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-browser:latest", + sandboxType = "custom", + securityLevel = "medium", + timeout = 30, + description = "Base Sandbox" ) -class MyCustomSandbox(Sandbox): - def __init__( - self, - sandbox_id: Optional[str] = None, - timeout: int = 3000, - base_url: Optional[str] = None, - bearer_token: Optional[str] = None, - ): - super().__init__( - sandbox_id, - timeout, - base_url, - bearer_token, - SandboxType(SANDBOXTYPE), - ) +public class CustomSandbox extends Sandbox { + + public CustomSandbox( + SandboxService managerApi, + String userId, + String sessionId) { + super(managerApi, userId, sessionId, "custom"); + } +} ``` ### Prepare Docker Image diff --git a/cookbook/en/sandbox/sandbox.md b/cookbook/en/sandbox/sandbox.md index 00327e2f..cdc0fa3c 100644 --- a/cookbook/en/sandbox/sandbox.md +++ b/cookbook/en/sandbox/sandbox.md @@ -5,16 +5,20 @@ AgentScope Runtime Java's Sandbox provides a **secure** and **isolated** environ ## Prerequisites ```{note} -The current sandbox environment uses Docker for isolation by default. Additionally, we support Kubernetes (K8s) and Alibaba Cloud Function Compute AgentRun as remote service backends. In the future, we plan to add more third-party hosting solutions in upcoming releases. -``` +The current sandbox environment uses Docker for isolation by default. In addition, we also support Kubernetes (K8s) and Alibaba Cloud Function Compute—AgentRun and FC—as remote service backends. In the future, we plan to integrate more third-party managed solutions in upcoming releases.``` >For devices using **Apple Silicon** (such as M1/M2), we recommend the following options to run **x86** Docker environments for maximum compatibility: > * Docker Desktop: Please refer to the [Docker Desktop Installation Guide](https://docs.docker.com/desktop/setup/install/mac-install/) to enable Rosetta2, ensuring compatibility with x86_64 images. > * Colima: Ensure Rosetta 2 support is enabled. You can start [Colima](https://github.com/abiosoft/colima) with the following command for compatibility: `colima start --vm-type=vz --vz-rosetta --memory 8 --cpu 1` - Docker (default) + +The following deployment backends are all provided as extension modules and must be included separately when used. + + - Kubernetes - Alibaba Cloud Function Compute AgentRun +- Function Compute (FC) ## Installation @@ -81,36 +85,31 @@ The previous section introduced tool-centric usage, while this section introduce You can create different types of sandboxes through the `sandbox` SDK. Use `SandboxService` to manage sandbox lifecycle, supporting session management and sandbox reuse. ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; +import com.google.gson.Gson; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; - -import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - // Create and start sandbox service - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - // Connect to sandbox (sandbox will be automatically deleted after execution) - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)){ + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (Sandbox sandbox = new BaseSandbox(sandboxService, "userId", "sessionId")) { Gson gson = new Gson(); String tools = gson.toJson(sandbox.listTools("")); System.out.println("Available tools: "); System.out.println(tools); - } - catch (Exception e) { + } catch (Exception e) { e.printStackTrace(); } } @@ -174,15 +173,34 @@ As you can see, the base sandbox provides two tools: **run command** and **execu * **Base Sandbox**: Used to run **Python code** or **Shell commands** in an isolated environment. ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)){ - System.out.println(sandbox.listTools("")); - if(sandbox instanceof BaseSandbox baseSandbox) { -String pythonResult = baseSandbox.runIpythonCell("print('Hello from the sandbox!')"); - System.out.println("Sandbox execution result: " + pythonResult); -String shellResult = baseSandbox.runShellCommand("echo Hello, World!"); - System.out.println("Shell command result: " + shellResult); +import io.agentscope.runtime.sandbox.box.BaseSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (BaseSandbox baseSandbox = new BaseSandbox(sandboxService, "userId", "sessionId")) { + System.out.println(baseSandbox.listTools("")); + String pythonResult = baseSandbox.runIpythonCell("print('Hello from the sandbox!')"); + System.out.println("Sandbox execution result: " + pythonResult); + String shellResult = baseSandbox.runShellCommand("echo Hello, World!"); + System.out.println("Shell command result: " + shellResult); + } catch (Exception e) { + e.printStackTrace(); + } } - } +} ``` * **GUI Sandbox**: Provides a **visual desktop environment** for mouse, keyboard, and screen-related operations. @@ -190,20 +208,40 @@ String shellResult = baseSandbox.runShellCommand("echo Hello, World!"); GUI Sandbox ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", GuiSandbox.class)){ -Gson gson = new Gson(); -String tools = gson.toJson(sandbox.listTools("")); -System.out.println("Available tools: "); -System.out.println(tools); - -if(sandbox instanceof GuiSandbox guiSandbox) { -String desktopUrl = guiSandbox.getDesktopUrl(); -System.out.println("GUI Desktop URL: " + desktopUrl); -String cursorPosition = guiSandbox.computerUse("get_cursor_position"); -System.out.println("Cursor Position: " + cursorPosition); -String screenShot = guiSandbox.computerUse("get_screenshot"); -System.out.println("Screenshot (base64): " + screenShot); -} +import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.GuiSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (GuiSandbox guiSandbox = new GuiSandbox(sandboxService, "userId", "sessionId")) { + Gson gson = new Gson(); + String tools = gson.toJson(guiSandbox.listTools("")); + System.out.println("Available tools: "); + System.out.println(tools); + + String desktopUrl = guiSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); + String cursorPosition = guiSandbox.computerUse("get_cursor_position"); + System.out.println("Cursor Position: " + cursorPosition); + String screenShot = guiSandbox.computerUse("get_screenshot"); + System.out.println("Screenshot (base64): " + screenShot); + } catch (Exception e) { + e.printStackTrace(); + } + } } ``` @@ -212,19 +250,39 @@ System.out.println("Screenshot (base64): " + screenShot); GUI Sandbox ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", FilesystemSandbox.class)){ -Gson gson = new Gson(); -String tools = gson.toJson(sandbox.listTools("")); - System.out.println("Available tools: "); - System.out.println(tools); - - if(sandbox instanceof FilesystemSandbox filesystemSandbox) { -String desktopUrl = filesystemSandbox.getDesktopUrl(); - System.out.println("GUI Desktop URL: " + desktopUrl); -String cursorPosition = filesystemSandbox.createDirectory("test"); - System.out.println("Created directory 'test' at: " + cursorPosition); +import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.FilesystemSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (FilesystemSandbox filesystemSandbox = new FilesystemSandbox(sandboxService, "userId", "sessionId")) { + Gson gson = new Gson(); + String tools = gson.toJson(filesystemSandbox.listTools("")); + System.out.println("Available tools: "); + System.out.println(tools); + + String desktopUrl = filesystemSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); + String result = filesystemSandbox.createDirectory("test"); + System.out.println("Created directory 'test' at: " + result); + } catch (Exception e) { + e.printStackTrace(); + } } - } +} ``` * **Browser Sandbox**: A GUI-based sandbox for browser operations. @@ -232,208 +290,217 @@ String cursorPosition = filesystemSandbox.createDirectory("test"); GUI Sandbox ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BrowserSandbox.class)){ -Gson gson = new Gson(); -String tools = gson.toJson(sandbox.listTools("")); - System.out.println("Available tools: "); - System.out.println(tools); - - if(sandbox instanceof BrowserSandbox browserSandbox) { -String desktopUrl = browserSandbox.getDesktopUrl(); - System.out.println("GUI Desktop URL: " + desktopUrl); -String navigateResult = browserSandbox.navigate("https://cn.bing.com"); - System.out.println("Navigate Result: " + navigateResult); - } - } -``` +import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.BrowserSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -* **TrainingSandbox**: Training and evaluation sandbox. For details, please refer to: [Training Sandbox](training_sandbox.md). +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); -```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ -String profileList = appWorldSandbox.getEnvProfile("appworld","train",null); - System.out.println("Profile List: " + profileList); - } else { - System.err.println("Failed to connect to TrainingSandbox."); + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (BrowserSandbox browserSandbox = new BrowserSandbox(sandboxService, "userId", "sessionId")) { + Gson gson = new Gson(); + String tools = gson.toJson(browserSandbox.listTools("")); + System.out.println("Available tools: "); + System.out.println(tools); + + String desktopUrl = browserSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); + String navigateResult = browserSandbox.navigate("https://cn.bing.com"); + System.out.println("Navigate Result: " + navigateResult); + } catch (Exception e) { + e.printStackTrace(); + } } - } +} ``` -* **Cloud Sandbox**: A cloud service-based sandbox environment that doesn't require local Docker containers. `CloudSandbox` is the base class for cloud sandboxes, providing a unified interface for cloud sandboxes. +* **Mobile Sandbox**: A sandbox based on an Android emulator, enabling mobile-specific operations such as tapping, swiping, text input, and taking screenshots. -```java -// CloudSandbox is an abstract base class, typically not used directly -// Please use specific cloud sandbox implementations, such as AgentBaySandbox -``` + Mobile Sandbox -* **AgentBay Sandbox**: A sandbox implementation based on AgentBay cloud service, supporting multiple image types (Linux, Windows, Browser, CodeSpace, Mobile, etc.) -```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", AgentBaySandbox.class)) { - System.out.println(sandbox.listTools()); - if (sandbox instanceof AgentBaySandbox agentBaySandbox) { - String pythonResult = agentBaySandbox.runIpythonCell("print('Hello from the sandbox!')"); - System.out.println("Sandbox execution result: " + pythonResult); - String shellResult = agentBaySandbox.runShellCommand("echo Hello, World!"); - System.out.println("Shell command result: " + shellResult); - } -} -``` +- **Runtime Environment Requirements** -**AgentBay Sandbox Features:** + - **Linux Host**: + When running the sandbox on a Linux host, the kernel must have the `binder` and `ashmem` modules loaded. If these modules are missing, install and load them by executing the following commands on the host: -* No local Docker required, completely cloud-based -* Supports multiple environment types -* Automatically manages session lifecycle -* Communicates directly with cloud services via API + ```bash + # 1. Install additional kernel modules + sudo apt update && sudo apt install -y linux-modules-extra-$(uname -r) + + # 2. Load kernel modules and create device nodes + sudo modprobe binder_linux devices="binder,hwbinder,vndbinder" + sudo modprobe ashmem_linux + ``` -> More sandbox types are under development, stay tuned! + - **Architecture Compatibility**: + When running on ARM64/aarch64 architectures (e.g., Apple M-series chips), compatibility or performance issues may occur. It is recommended to run the sandbox on an x86_64 architecture host. -### Adding MCP Servers to Sandbox +```java +import io.agentscope.runtime.sandbox.box.MobileSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -MCP (Model Context Protocol) is a standardized protocol that enables AI applications to securely connect to external data sources and tools. By integrating MCP servers into your sandbox, you can extend sandbox functionality with specialized tools and services without compromising security. +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); -Sandboxes support MCP server integration through the `add_mcp_servers` method. After adding, you can use `list_tools` to discover available tools and use `call_tool` to execute them. + try (MobileSandbox mobileSandbox = new MobileSandbox(sandboxService, "userId", "sessionId")) { + System.out.println("Listing all available tools:"); + System.out.println(mobileSandbox.listTools("")); -```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)) { -String mcpServerConfig = """ - { - "mcpServers": { - "time": { - "command": "uvx", - "args": [ - "mcp-server-time", - "--local-timezone=America/New_York" - ] - } - } - } - """; + System.out.println("Getting screen resolution:"); + System.out.println(mobileSandbox.mobileGetScreenResolution()); -Gson gson = new Gson(); -Type mcpServerType = new TypeToken>() { -}.getType(); -Map serverConfigMap = gson.fromJson(mcpServerConfig, mcpServerType); + System.out.println("Tapping at coordinates (500, 1000):"); + System.out.println(mobileSandbox.mobileTap(500, 1000)); - // Add MCP server to sandbox - sandbox.addMcpServers(serverConfigMap); + System.out.println("Inputting text:"); + System.out.println(mobileSandbox.mobileInputText("Greetings from AgentScope!")); - // List all available tools (now includes MCP tools) -String tools = gson.toJson(sandbox.listTools("")); - System.out.println("Available tools: "); - System.out.println(tools); + System.out.println("Sending HOME key event (key code 3):"); + System.out.println(mobileSandbox.mobileKeyEvent(3)); - // Use the time tool provided by MCP server -String result = sandbox.callTool("get_current_time", Map.of("timezone", "America/New_York")); - System.out.println("Tool call result: "); - System.out.println(result); + System.out.println("Taking a screenshot:"); + String screenshotResult = mobileSandbox.mobileGetScreenshot(); + System.out.println(screenshotResult); + } catch (Exception e) { + e.printStackTrace(); + } + } } ``` -### Connecting to Remote Sandbox - -> Remote sandbox deployment is particularly suitable for: -> * Separating compute-intensive tasks to dedicated servers -> * Multiple clients sharing the same sandbox environment -> * Developing on resource-constrained local machines while executing on high-performance servers -> * K8s cluster sandbox service deployment -> -> For more advanced usage of sandbox-server, please see [Tool Sandbox Advanced Usage](advanced.md) for detailed instructions. +* **TrainingSandbox**: Training and evaluation sandbox. For details, please refer to: [Training Sandbox](training_sandbox.md). -You can start a sandbox server on your local machine or a different machine for remote access. You can first start a runtime as a remote sandbox manager. +```java +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -To connect to a remote sandbox service, simply add the actual startup address of the remote runtime when building managerConfig. Other operations are the same as local sandbox. Sandbox management and tool calls will automatically be forwarded to the remote runtime for processing: +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); -```java -ManagerConfig managerConfig = ManagerConfig.builder() - .baseUrl("http://remote-host:port") - .build(); + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); + } catch (Exception e) { + e.printStackTrace(); + } + } +} ``` -## Sandbox Service +* **Cloud Sandbox**: A cloud service-based sandbox environment that doesn't require local Docker containers. `CloudSandbox` is the base class for cloud sandboxes, providing a unified interface for cloud sandboxes. -### Managing Sandboxes with Sandbox Service +```java +// CloudSandbox is an abstract base class, typically not used directly +// Please use specific cloud sandbox implementations, such as AgentBaySandbox +``` -`SandboxService` provides a unified sandbox management interface, supporting management of sandbox environments for different user sessions through `session_id` and `user_id`. Using `SandboxService` gives you better control over sandbox lifecycle and enables sandbox reuse. +* **AgentBay Sandbox**: A sandbox implementation based on AgentBay cloud service, supporting multiple image types (Linux, Windows, Browser, CodeSpace, Mobile, etc.) ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.box.AgentBaySandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - // Create and start sandbox service - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .agentBayApiKey(System.getenv("AGENTBAY_API_KEY")) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - try { - // Connect to sandbox, specify the required sandbox type - Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class); - if(sandbox instanceof BaseSandbox baseSandbox) { - // Call tool methods directly on sandbox instance - String pythonResult = baseSandbox.runIpythonCell("a=1"); - System.out.println("Sandbox execution result: " + pythonResult); - } - // Using the same session_id and user_id will reuse the same sandbox instance - sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class); - if(sandbox instanceof BaseSandbox baseSandbox) { - // Variable a still exists because the same sandbox is reused - String pythonResult = baseSandbox.runIpythonCell("print(a)"); - System.out.println("Sandbox execution result: " + pythonResult); - } - // Stop sandbox service - sandbox.close(); - } - catch (Exception e) { - e.printStackTrace(); + // Connect to an AgentBay sandbox (automatically deleted after execution) + try (AgentBaySandbox agentBaySandbox = new AgentBaySandbox(sandboxService, "user", "session", "linux_latest")) { + System.out.println(agentBaySandbox.listTools()); + String pythonResult = agentBaySandbox.runIpythonCell("print('Hello from the sandbox!')"); + System.out.println("Sandbox execution result: " + pythonResult); + String shellResult = agentBaySandbox.runShellCommand("echo Hello, World!"); + System.out.println("Shell command result: " + shellResult); } } } ``` -### Adding MCP Servers with Sandbox Service +**AgentBay Sandbox Features:** + +* No local Docker required, completely cloud-based +* Supports multiple environment types +* Automatically manages session lifecycle +* Communicates directly with cloud services via API + +> More sandbox types are under development, stay tuned! + +### Adding MCP Servers to Sandbox + +MCP (Model Context Protocol) is a standardized protocol that enables AI applications to securely connect to external data sources and tools. By integrating MCP servers into your sandbox, you can extend sandbox functionality with specialized tools and services without compromising security. + +Sandboxes support MCP server integration through the `add_mcp_servers` method. After adding, you can use `list_tools` to discover available tools and use `call_tool` to execute them. ```java import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import java.lang.reflect.Type; import java.util.Map; public class Main { public static void main(String[] args) { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)) { + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (Sandbox sandbox = new BaseSandbox(sandboxService, "userID", "sessionID")) { String mcpServerConfig = """ - { + { "mcpServers": { "time": { "command": "uvx", @@ -447,19 +514,18 @@ public class Main { """; Gson gson = new Gson(); - Type mcpServerType = new TypeToken>() { - }.getType(); + Type mcpServerType = new TypeToken>() {}.getType(); Map serverConfigMap = gson.fromJson(mcpServerConfig, mcpServerType); - // Add MCP server to sandbox + // Add MCP servers to the sandbox sandbox.addMcpServers(serverConfigMap); - // List all available tools (now includes MCP tools) + // List all available tools (now including MCP tools) String tools = gson.toJson(sandbox.listTools("")); System.out.println("Available tools: "); System.out.println(tools); - // Use the time tool provided by MCP server + // Call an MCP-provided tool String result = sandbox.callTool("get_current_time", Map.of("timezone", "America/New_York")); System.out.println("Tool call result: "); System.out.println(result); @@ -470,40 +536,64 @@ public class Main { } ``` -### Connecting to Remote Sandbox with Sandbox Service +### Connecting to Remote Sandbox + +> Remote sandbox deployment is particularly suitable for: +> * Separating compute-intensive tasks to dedicated servers +> * Multiple clients sharing the same sandbox environment +> * Developing on resource-constrained local machines while executing on high-performance servers +> * K8s cluster sandbox service deployment +> +> For more advanced usage of sandbox-server, please see [Tool Sandbox Advanced Usage](advanced.md) for detailed instructions. + +You can start a sandbox server on your local machine or a different machine for remote access. You can first start a runtime as a remote sandbox manager. + +To connect to a remote sandbox service, simply add the actual startup address of the remote runtime when building managerConfig. Other operations are the same as local sandbox. Sandbox management and tool calls will automatically be forwarded to the remote runtime for processing: + +```java +ManagerConfig managerConfig = ManagerConfig.builder() + .baseUrl("http://remote-host:port") + .build(); +``` + +## Sandbox Service + +### Managing Sandboxes with Sandbox Service + +`SandboxService` provides a unified sandbox management interface, supporting management of sandbox environments for different user sessions through `session_id` and `user_id`. Using `SandboxService` gives you better control over sandbox lifecycle and enables sandbox reuse. ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - // Create and start sandbox service + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .baseUrl("http://remote-host:port") + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); try { - // Connect to sandbox, specify the required sandbox type - Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class); - if(sandbox instanceof BaseSandbox baseSandbox) { - // Call tool methods directly on sandbox instance - String pythonResult = baseSandbox.runIpythonCell("a=1"); - System.out.println("Sandbox execution result: " + pythonResult); - } - // Stop sandbox service - sandbox.close(); - } - catch (Exception e) { + // Connect to a sandbox, specifying the desired sandbox type + BaseSandbox baseSandbox = new BaseSandbox(sandboxService, "userId", "sessionId"); + // Call tool methods directly on the sandbox instance + String pythonResult = baseSandbox.runIpythonCell("a=1"); + System.out.println("Sandbox execution result: " + pythonResult); + + // Using the same session_id and user_id reuses the same sandbox instance + baseSandbox = new BaseSandbox(sandboxService, "userId", "sessionId"); + pythonResult = baseSandbox.runIpythonCell("print(a)"); + System.out.println("Sandbox execution result: " + pythonResult); + + // Close the sandbox explicitly + baseSandbox.close(); + } catch (Exception e) { e.printStackTrace(); } } diff --git a/cookbook/en/sandbox/training_sandbox.md b/cookbook/en/sandbox/training_sandbox.md index c14f511c..dd6acfaa 100644 --- a/cookbook/en/sandbox/training_sandbox.md +++ b/cookbook/en/sandbox/training_sandbox.md @@ -36,38 +36,27 @@ docker pull agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtim You can verify that everything is set up correctly by calling `getEnvProfile`. If correct, it will return dataset IDs: ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; - -import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - // Create and start sandbox service - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - // Connect to sandbox (sandbox will be automatically deleted after execution) - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ - String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); - System.out.println("Profile List: " + profileList); - } else { - System.err.println("Failed to connect to TrainingSandbox."); - } - } - catch (Exception e) { + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); + } catch (Exception e) { e.printStackTrace(); } } @@ -97,13 +86,9 @@ After building the Docker image, we can first view the dataset samples. For example, we can use the `getEnvProfile` method to get a list of training IDs. ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ - String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); - System.out.println("Profile List: " + profileList); - } else { - System.err.println("Failed to connect to TrainingSandbox."); - } +try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); } ``` @@ -114,25 +99,56 @@ One query can create multiple instances, and each instance uniquely corresponds The prompt (`system prompt`) and actual question (`user prompt`) provided by the training set will be returned as a `Message List`, specifically located in the `state` of the return value. ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ - String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); - System.out.println("Profile List: " + profileList); +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; - Gson gson = new Gson(); - Type listType = new TypeToken>(){}.getType(); - List list = gson.fromJson(profileList, listType); - String initResponse = appWorldSandbox.createInstance("appworld", list.get(0)); - Type instanceType = new TypeToken>(){}.getType(); - Map instance = gson.fromJson(initResponse, instanceType); - String instanceInfo = instance.get("info").toString(); - Type infoType = new TypeToken>(){}.getType(); - Map infoMap = gson.fromJson(instanceInfo, infoType); - String instanceId = (String) infoMap.get("instance_id"); - String query = instance.get("state").toString(); - System.out.println("Created instance " + instanceId + " with query: " + query); - } else { - System.err.println("Failed to connect to TrainingSandbox."); +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +public class Main { + public static void main(String[] args) { + // Create and start the sandbox service + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + + // Connect to a sandbox (the sandbox will be automatically deleted after execution) + try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); + + Gson gson = new Gson(); + Type listType = new TypeToken>() {}.getType(); + List profiles = gson.fromJson(profileList, listType); + + if (profiles.isEmpty()) { + System.out.println("No profiles available."); + return; + } + + String initResponse = appWorldSandbox.createInstance("appworld", profiles.get(0)); + Type instanceType = new TypeToken>() {}.getType(); + Map instance = gson.fromJson(initResponse, instanceType); + + String instanceInfoStr = instance.get("info").toString(); + Type infoType = new TypeToken>() {}.getType(); + Map infoMap = gson.fromJson(instanceInfoStr, infoType); + String instanceId = (String) infoMap.get("instance_id"); + + String query = instance.get("state").toString(); + System.out.println("Created instance " + instanceId + " with query: " + query); + } catch (Exception e) { + e.printStackTrace(); + } } } ``` diff --git a/cookbook/en/service/sandbox.md b/cookbook/en/service/sandbox.md index 9053e72c..40a0aafe 100644 --- a/cookbook/en/service/sandbox.md +++ b/cookbook/en/service/sandbox.md @@ -29,35 +29,32 @@ In the **AgentScope** framework, we can use **encapsulated sandbox methods** (`T ```java import io.agentscope.core.tool.Toolkit; import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BrowserSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - // 1. Start service (usually managed by Runner/Engine) - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); + // 1. Start the sandbox service + BaseClientStarter clientStarter = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientStarter) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - // 2. Connect or create sandbox (here creating browser type) - Sandbox sandbox = sandboxService.connect("TestSession", "User1", BrowserSandbox.class); + // 2. Connect to or create a sandbox (here, a browser-type sandbox is created) + Sandbox sandbox = new BrowserSandbox(sandboxService, "user", "session"); - // 3. Get tool methods and register in Agent's Toolkit + // 3. Obtain tool methods and register them into the Agent's Toolkit Toolkit toolkit = new Toolkit(); toolkit.registerTool(ToolkitInit.BrowserNavigateTool(sandbox)); toolkit.registerTool(ToolkitInit.BrowserTakeScreenshotTool(sandbox)); - // After this, Agents can call these tools to perform safe operations in the sandbox + // From this point on, the Agent can safely invoke these tools within the sandbox } } ``` @@ -73,23 +70,22 @@ public class Main { ### 2. **Remote API Mode** -- **Characteristics**: Connect to remote sandbox instances through sandbox management API (`SandboxManager`). -- **Configuration**: `baseUrl="http://host:port"`, `bearerToken="..."` +- **Features**: Connects to remote sandbox instances via the Sandbox Management API (`SandboxService`).- **Configuration**: `baseUrl="http://host:port"`, `bearerToken="..."` - **Advantages**: Can share environments across processes/machines, supports distributed scaling. - **Disadvantages**: Requires deployment and operation of remote sandbox management services. ### Supported Sandbox Types -| Type Value | Function Description | Common Use Cases | -| ------------ | ---------------------------- | ----------------------------------------- | -| `DUMMY` | Empty implementation/placeholder sandbox | Test workflows, simulate sandbox interface without executing actual operations | -| `BASE` | Basic sandbox environment | General tool runtime environment | -| `BROWSER` | Browser sandbox | Web navigation, screenshots, data scraping | -| `FILESYSTEM` | File system sandbox | Read/write/manage files in a secure isolated file system | -| `GUI` | Graphical interface sandbox | Interact with GUI applications (click, input, screenshot) | -| `APPWORLD` | Application world simulation sandbox | Simulate cross-application interactions in virtual environments | -| `BFCL` | BFCL (specific business domain execution environment) | Run business process scripts (depends on implementation) | -| `AGENTBAY` | AgentBay session-based sandbox | Persistent environment for multi-agent collaboration or complex task orchestration | +| Type Value | Description | Common Use Cases | +|--------------|----------------------------------|-------------------------------------------| +| `BASE` | Basic sandbox environment | General-purpose tool execution | +| `BROWSER` | Browser sandbox | Web navigation, screenshots, data scraping| +| `FILESYSTEM` | Filesystem sandbox | Secure read/write operations in isolated filesystem | +| `GUI` | Graphical User Interface sandbox | Interacting with GUI apps (clicks, input, screenshots) | +| `APPWORLD` | Application World simulation sandbox | Simulating cross-application interactions in a virtual environment | +| `BFCL` | BFCL (Business Function Computing Layer) sandbox | Running domain-specific workflow scripts (implementation-dependent) | +| `AGENTBAY` | AgentBay session-aware sandbox | Persistent environments for multi-agent collaboration or complex task orchestration | +| `MOBILE` | Mobile device sandbox | Simulating mobile device operations | ## Runtime Mode Switching Examples @@ -98,10 +94,9 @@ public class Main { ```java // Local mode (default uses local Docker) ManagerConfig managerConfig = ManagerConfig.builder() - .build(); -SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) -); + .build(); +SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); Sandbox sandbox = sandboxService.connect("DevSession", "User1", BrowserSandbox.class); @@ -112,15 +107,13 @@ Sandbox sandbox = sandboxService.connect("DevSession", "User1", BrowserSandbox.c ```java // Local mode (default uses local Docker) ManagerConfig managerConfig = ManagerConfig.builder() - .baseUrl("https://sandbox-manager.com") - .bearerToken("YOUR_AUTH_TOKEN") - .build(); -SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) -); + .baseUrl("https://sandbox-manager.com") + .bearerToken("YOUR_AUTH_TOKEN") + .build(); +SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); -Sandbox sandbox = sandboxService.connect("ProdSession", "UserABC", BrowserSandbox.class); +Sandbox sandbox = new BrowserSandbox(sandboxService, "ProdSession", "UserABC"); ``` ### Release Environment @@ -128,8 +121,11 @@ Sandbox sandbox = sandboxService.connect("ProdSession", "UserABC", BrowserSandbo Explicitly release resources when the session ends: ```java -// Release directly through container name -sandboxService.getManagerApi().release("container_name"); +// Release sandbox directly by container ID +sandboxService.release("container_id"); + +// Or release via the sandbox instance +sandbox.release(); ``` ## Selection Recommendations @@ -138,7 +134,7 @@ sandboxService.getManagerApi().release("container_name"); - Embedded mode (`baseUrl=null`) - Choose `BROWSER`/`BASE` types as needed - Production Environment / Multi-User Distributed: - - Remote API mode (requires deploying `SandboxManager` service) + - Remote API mode (requires deploying the `SandboxService` via the `web` module) - Consider clustering and authentication mechanisms (`bearerToken`) - High Security or Isolation Requirements: - Create independent sandboxes for different user sessions diff --git a/cookbook/en/service/service.md b/cookbook/en/service/service.md index 278bbec2..99bf4b66 100644 --- a/cookbook/en/service/service.md +++ b/cookbook/en/service/service.md @@ -68,20 +68,34 @@ For more available service types and detailed usage, see [Session History Servic In the AgentScope framework, bind the session history service to the `LongTermMemory` module through Runtime's `AgentScopeLongTermMemory` adapter: ```java -import io.agentscope.runtime.adapters.agentscope.memory.LongTermMemoryAdapter; -import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; -import io.agentscope.runtime.engine.services.memory.service.MemoryService; +import io.agentscope.core.tool.Toolkit; +import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; +import io.agentscope.runtime.sandbox.box.BrowserSandbox; +import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - MemoryService memoryService = new InMemoryMemoryService(); - LongTermMemoryAdapter longTermMemory = null; - - longTermMemory = new LongTermMemoryAdapter( - memoryService, - "User1", - "Test Session" - ); + // Create and start the sandbox service using Docker as the backend + BaseClientStarter clientStarter = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientStarter) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + + // Connect to (or create) a browser-type sandbox with specified user and session IDs + Sandbox sandbox = new BrowserSandbox(sandboxService, "user_1", "session_1"); + + // Initialize a toolkit and register browser-related tools bound to this sandbox + Toolkit toolkit = new Toolkit(); + toolkit.registerTool(ToolkitInit.BrowserNavigateTool(sandbox)); + toolkit.registerTool(ToolkitInit.BrowserTakeScreenshotTool(sandbox)); + + // The Agent can now use these tools to perform safe, isolated browser operations } } ``` diff --git a/cookbook/zh/deployment/agent_app.md b/cookbook/zh/deployment/agent_app.md index 670b5b82..d8992b92 100644 --- a/cookbook/zh/deployment/agent_app.md +++ b/cookbook/zh/deployment/agent_app.md @@ -349,10 +349,9 @@ import io.agentscope.runtime.app.AgentApp; import io.agentscope.runtime.engine.services.agent_state.InMemoryStateService; import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.jetbrains.annotations.NotNull; /** @@ -362,7 +361,7 @@ import org.jetbrains.annotations.NotNull; *
    *
  • 会话状态管理(内存存储)
  • *
  • 短期记忆(会话历史)与长期记忆(用户级记忆库)
  • - *
  • Python 沙箱工具支持(通过 Kubernetes 启动隔离执行环境)
  • + *
  • Python 沙箱工具支持(通过 Docker 启动隔离执行环境)
  • *
  • 通过 HTTP 接口提供流式智能体推理服务
  • *
* @@ -401,19 +400,18 @@ public class AgentScopeDeployExample { } /** - * 构建沙箱服务实例,默认使用 Kubernetes 客户端配置。 + * 构建沙箱服务实例,默认使用 Docker 客户端配置。 * * @return 配置完成的 SandboxService 实例 */ @NotNull private static SandboxService buildSandboxService() { - // 使用默认 Kubernetes 配置(实际部署时可替换为真实集群配置) - var clientConfig = KubernetesClientConfig.builder().build(); + var clientConfig = DockerClientStarter.builder().build(); var managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) - .build(); + .clientStarter(clientConfig) + .build(); - return new SandboxService(new SandboxManager(managerConfig)); + return new SandboxService(managerConfig); } } ``` diff --git a/cookbook/zh/quickstart.md b/cookbook/zh/quickstart.md index 24fd76ce..5c071cd8 100644 --- a/cookbook/zh/quickstart.md +++ b/cookbook/zh/quickstart.md @@ -57,6 +57,7 @@ import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; import io.agentscope.runtime.engine.schemas.AgentRequest; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.SandboxService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; @@ -300,30 +301,32 @@ import io.agentscope.runtime.app.AgentApp; import io.agentscope.runtime.engine.services.agent_state.InMemoryStateService; import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.jetbrains.annotations.NotNull; ``` #### 2.2 构建 SandboxService 沙箱管理服务 ```java -private static SandboxService buidSandboxService() { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); +@NotNull +private static SandboxService buildSandboxService() { + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - return new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService( + managerConfig + ); + sandboxService.start(); + return sandboxService; } ``` -* 沙箱运行环境支持 **Docker**、**K8s **以及 **AgentRun**,未配置 `clientConfig` 默认使用**本地 Docker **作为运行环境 -* 使用`managerConfig`构建 **SandboxManager**,并由此构建 **SandboxService** +* 沙箱运行环境支持 **Docker**、**K8s **、 **AgentRun** 以及 **FC**,未配置 `clientStarter` 默认使用**本地 Docker **作为运行环境 +* 使用`managerConfig`构建 **SandboxService** #### 2.3 构建 AgentApp @@ -343,6 +346,10 @@ agentHandler.setSandboxService(buidSandboxService()); ```java AgentApp agentApp = new AgentApp(agentHandler); +agentApp.cors(registry -> registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE") + .allowCredentials(true)); agentApp.run(10001); ``` diff --git a/cookbook/zh/sandbox/advanced.md b/cookbook/zh/sandbox/advanced.md index e9e15869..352fce9a 100644 --- a/cookbook/zh/sandbox/advanced.md +++ b/cookbook/zh/sandbox/advanced.md @@ -1,20 +1,18 @@ # 工具沙箱高级用法 ->本节介绍沙箱的高级用法。我们强烈建议在继续之前先完成上一节的基础教程[沙箱](sandbox.md)。 +> 本节介绍沙箱的高级用法。我们强烈建议在继续之前先完成上一节的基础教程[沙箱](sandbox.md)。 ## 沙箱管理器配置参考 #### ManagerConfig 配置 -| Parameter | Type | Description | Default | Notes | -| --------------------- | -------------------- | ----------------------------------- | ----------------------- | ------------------------------------------------------------ | -| `defaultSandboxType` | `List` | 默认沙箱类型(可多个) | `SandboxType.BASE` | 可以是单个类型,也可以是多个类型的列表,从而启用多个独立的沙箱预热池。合法取值包括 `BASE`、`BROWSER`、`FILESYSTEM`、`GUI` 等 | -| `bearerToken` | `String` | 调用远程runtime沙箱的身份验证令牌 | `null` | 如果设置为 `null`,将在连接的时候不会进行身份验证 | -| `baseUrl` | `String` | 调用远程runtime沙箱的服务器绑定地址 | `null` | 如果设置为 `null`,将默认使用本地沙箱管理 | -| `containerDeployment` | `BaseClientConfig` | 容器运行时 | `DockerClientConfig` | 目前支持 `Docker`、`K8s` 和 `AgentRun` | -| `poolSize` | `int` | 预热容器池大小 | `0` | 缓存的容器以实现更快启动。 `poolSize` 参数控制预创建并缓存在就绪状态的容器数量。当用户请求新沙箱时,系统将首先尝试从这个预热池中分配,相比从零开始创建容器显著减少启动时间。例如,使用 `poolSize=10`,系统维护 10 个就绪容器,可以立即分配给新请求 | -| `fileSystemConfig` | `FileSystemConfig` | 容器文件系统配置 | `LocalFileSystemConfig` | 管理容器文件系统的下载方式,默认使用`本地文件系统`,也可以使用 `oss` | -| `redisConfig` | `RedisManagerConfig` | redis支持配置 | `null` | 启用 Redis 支持,分布式部署或工作进程数大于 `1` 时必需,默认不启用 | +| Parameter | Type | Description | Default | Notes | +| -------------------- | ------------------- | ----------------------------------- | --------------------- | ------------------------------------------------------------ | +| `bearerToken` | `String` | 调用远程runtime沙箱的身份验证令牌 | `null` | 如果设置为 `null`,将在连接的时候不会进行身份验证 | +| `baseUrl` | `String` | 调用远程runtime沙箱的服务器绑定地址 | `null` | 如果设置为 `null`,将默认使用本地沙箱管理 | +| `clientStarter` | `BaseClientStarter` | 容器运行时 | `DockerClientStarter` | 目前支持 `Docker`、`K8s` 、`AgentRun`和`FC` | +| `sandboxMap` | `SandboxMap` | 容器管理 | `InMemorySandboxMap` | 容器的管理,默认使用本地存储,引入 `redis-extension` 可以使用 `redis` 作为管理后端 | +| `containerPrefixKey` | `String` | 容器名称前缀 | `sandbox_container_` | 创建的容器名称前缀 | #### Redis 配置 @@ -23,7 +21,7 @@ > - **单个工作进程(`WORKERS=1`)**:Redis 是可选的。系统可以使用内存缓存来管理沙箱状态,这更简单且延迟更低。 > - **多个工作进程(`WORKERS>1`)**:需要 Redis 来在工作进程间共享沙箱状态并确保一致性。 -Redis 为沙箱状态和状态管理提供缓存。如果只有一个工作进程,您可以使用内存缓存: +Redis 为沙箱状态和状态管理提供缓存。如果只有一个工作进程,您可以使用内存缓存,您需要配置 `RedisManagerConfig` 并使用它创建一个 `RedisSandboxMap`,以下是 `RedisManagerConfig` 需要配置的参数: | Parameter | Description | Default | Notes | | ----------------------- | ---------------- | ------------------------------------------- | ---------------- | @@ -37,9 +35,21 @@ Redis 为沙箱状态和状态管理提供缓存。如果只有一个工作进 #### FileSystemConfig 配置 +目前只支持在本地 Docker 下实现文件目录的挂载,支持三种挂载: + +* 拷贝挂载(推荐):配置 `storageFolderPath`、`mountDir` 两个属性,在创建容器的时候,会首先将存储目录中的内容拷贝到挂载路径,并让容器挂载挂载路径,防止对原始文件造成污染,在容器关闭的时候,会将挂载路径的内容拷贝回去 +* 只读挂载:配置 `readonlyMounts`,容器会直接挂载本地文件系统 +* 可读写零拷贝挂载(谨慎使用):配置 `nonCopyMount`,容器会直接挂载本地文件系统,并拥有对于该文件夹的读写权限 + +在使用的时候,您需要把 `FileSystemConfig` 传递给 `Sandbox` 的 `fileSystemConfig` 参数。 + +##### 存储后端 + +###### 本地存储 + 默认使用 `LocalFileSystemConfig`,本地条件下无需配置,使用 `oss` 情况下需配置 -##### OSS 配置 +###### OSS 存储 使用[阿里云对象存储服务](https://www.aliyun.com/product/oss)进行分布式文件存储: @@ -177,29 +187,24 @@ pip install -e . ```java import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; + @RegisterSandbox( - imageName = "YOUR-IMAGE-NAME", - sandboxType = SandboxType.CUSTOM, + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-browser:latest", + sandboxType = "custom", securityLevel = "medium", timeout = 30, - description = "YOUR Sandbox" + description = "Base Sandbox" ) public class CustomSandbox extends Sandbox { - public CustomSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); - } - public CustomSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, - String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.CUSTOM, timeout); + String sessionId) { + super(managerApi, userId, sessionId, "custom"); } } ``` diff --git a/cookbook/zh/sandbox/sandbox.md b/cookbook/zh/sandbox/sandbox.md index 0e1da64b..7224ddab 100644 --- a/cookbook/zh/sandbox/sandbox.md +++ b/cookbook/zh/sandbox/sandbox.md @@ -5,7 +5,7 @@ AgentScope Runtime Java 的 Sandbox 提供了一个**安全**且**隔离**的环 ## 前提条件 ```{note} -当前的沙箱环境默认使用 Docker 进行隔离。此外,我们还支持 Kubernetes (K8s) 以及阿里云函数计算 AgentRun 作为远程服务后端。未来,我们计划在即将发布的版本中加入更多第三方托管解决方案。 +当前的沙箱环境默认使用 Docker 进行隔离。此外,我们还支持 Kubernetes (K8s) 以及阿里云函数计算 AgentRun、FC 作为远程服务后端。未来,我们计划在即将发布的版本中加入更多第三方托管解决方案。 ``` @@ -15,8 +15,13 @@ AgentScope Runtime Java 的 Sandbox 提供了一个**安全**且**隔离**的环 - Docker(默认) + +以下几种部署后端均为扩展模块,使用时需要单独引入 + + - Kubernetes - 阿里云函数计算 AgentRun +- 函数计算 FC ## 安装 @@ -24,10 +29,6 @@ AgentScope Runtime Java 的 Sandbox 提供了一个**安全**且**隔离**的环 首先,安装 AgentScope Runtime: -```bash -pip install agentscope-runtime -``` - ### 准备 Docker 镜像 沙箱为不同功能使用不同的 Docker 镜像。您可以只拉取需要的镜像,或者拉取所有镜像以获得完整功能: @@ -80,34 +81,30 @@ docker pull agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtim 前面的部分介绍了以工具为中心的使用方法,而本节介绍以沙箱为中心的使用方法。 -您可以通过`sandbox` SDK创建不同类型的沙箱。通过 `SandboxService` 管理沙箱生命周期,支持会话管理和沙箱复用。 +您可以通过 `sandbox` SDK创建不同类型的沙箱。通过 `SandboxService` 管理沙箱生命周期,支持会话管理和沙箱复用。 ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; +import com.google.gson.Gson; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; - -import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { // 创建并启动沙箱服务 - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); // 连接沙箱(沙箱会在执行后自动删除) - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)){ + try (Sandbox sandbox = new BaseSandbox(sandboxService, "userId", "sessionId")) { Gson gson = new Gson(); String tools = gson.toJson(sandbox.listTools("")); System.out.println("Available tools: "); @@ -178,36 +175,77 @@ public class Main { * **基础沙箱(Base Sandbox)**:用于在隔离环境中运行 **Python 代码** 或 **Shell 命令**。 ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)){ - System.out.println(sandbox.listTools("")); - if(sandbox instanceof BaseSandbox baseSandbox) { -String pythonResult = baseSandbox.runIpythonCell("print('Hello from the sandbox!')"); - System.out.println("Sandbox execution result: " + pythonResult); -String shellResult = baseSandbox.runShellCommand("echo Hello, World!"); - System.out.println("Shell command result: " + shellResult); +import io.agentscope.runtime.sandbox.box.BaseSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + +// 连接沙箱(沙箱会在执行后自动删除) + try (BaseSandbox baseSandbox = new BaseSandbox(sandboxService, "userId", "sessionId")) { + System.out.println(baseSandbox.listTools("")); + String pythonResult = baseSandbox.runIpythonCell("print('Hello from the sandbox!')"); + System.out.println("Sandbox execution result: " + pythonResult); + String shellResult = baseSandbox.runShellCommand("echo Hello, World!"); + System.out.println("Shell command result: " + shellResult); + } + catch (Exception e) { + e.printStackTrace(); + } } - } +} ``` * **GUI 沙箱 (GUI Sandbox)**: 提供**可视化桌面环境**,可执行鼠标、键盘以及屏幕相关操作。 GUI Sandbox -```json -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", GuiSandbox.class)){ -Gson gson = new Gson(); -String tools = gson.toJson(sandbox.listTools("")); -System.out.println("Available tools: "); -System.out.println(tools); - -if(sandbox instanceof GuiSandbox guiSandbox) { -String desktopUrl = guiSandbox.getDesktopUrl(); -System.out.println("GUI Desktop URL: " + desktopUrl); -String cursorPosition = guiSandbox.computerUse("get_cursor_position"); -System.out.println("Cursor Position: " + cursorPosition); -String screenShot = guiSandbox.computerUse("get_screenshot"); -System.out.println("Screenshot (base64): " + screenShot); -} +```java +import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.GuiSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + +// 连接沙箱(沙箱会在执行后自动删除) + try (GuiSandbox guiSandbox = new GuiSandbox(sandboxService, "userId", "sessionId")) { + Gson gson = new Gson(); + String tools = gson.toJson(guiSandbox.listTools("")); + System.out.println("Available tools: "); + System.out.println(tools); + + String desktopUrl = guiSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); + String cursorPosition = guiSandbox.computerUse("get_cursor_position"); + System.out.println("Cursor Position: " + cursorPosition); + String screenShot = guiSandbox.computerUse("get_screenshot"); + System.out.println("Screenshot (base64): " + screenShot); + } + catch (Exception e) { + e.printStackTrace(); + } + } } ``` @@ -216,226 +254,258 @@ System.out.println("Screenshot (base64): " + screenShot); GUI Sandbox ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", FilesystemSandbox.class)){ -Gson gson = new Gson(); -String tools = gson.toJson(sandbox.listTools("")); - System.out.println("Available tools: "); - System.out.println(tools); - - if(sandbox instanceof FilesystemSandbox filesystemSandbox) { -String desktopUrl = filesystemSandbox.getDesktopUrl(); - System.out.println("GUI Desktop URL: " + desktopUrl); -String cursorPosition = filesystemSandbox.createDirectory("test"); - System.out.println("Created directory 'test' at: " + cursorPosition); - } - } -``` +import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.FilesystemSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -* **浏览器沙箱(Browser Sandbox)**: 基于 GUI 的沙箱,可进行浏览器操作。 +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); - GUI Sandbox +// 连接沙箱(沙箱会在执行后自动删除) + try (FilesystemSandbox filesystemSandbox = new FilesystemSandbox(sandboxService, "userId", "sessionId")) { + Gson gson = new Gson(); + String tools = gson.toJson(filesystemSandbox.listTools("")); + System.out.println("Available tools: "); + System.out.println(tools); -```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BrowserSandbox.class)){ -Gson gson = new Gson(); -String tools = gson.toJson(sandbox.listTools("")); - System.out.println("Available tools: "); - System.out.println(tools); - - if(sandbox instanceof BrowserSandbox browserSandbox) { -String desktopUrl = browserSandbox.getDesktopUrl(); - System.out.println("GUI Desktop URL: " + desktopUrl); -String navigateResult = browserSandbox.navigate("https://cn.bing.com"); - System.out.println("Navigate Result: " + navigateResult); + String desktopUrl = filesystemSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); + String cursorPosition = filesystemSandbox.createDirectory("test"); + System.out.println("Created directory 'test' at: " + cursorPosition); + } + catch (Exception e) { + e.printStackTrace(); + } } - } +} ``` -* **TrainingSandbox**:训练评估沙箱,详情请参考:[训练用沙箱](training_sandbox.md)。 - -```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ -String profileList = appWorldSandbox.getEnvProfile("appworld","train",null); - System.out.println("Profile List: " + profileList); - } else { - System.err.println("Failed to connect to TrainingSandbox."); - } - } -``` +* **浏览器沙箱(Browser Sandbox)**: 基于 GUI 的沙箱,可进行浏览器操作。 -* **云沙箱(Cloud Sandbox)**:基于云服务的沙箱环境,无需本地 Docker 容器。`CloudSandbox` 是云沙箱的基类,提供了云沙箱的统一接口 + GUI Sandbox ```java -// CloudSandbox 是抽象基类,通常不直接使用 -// 请使用具体的云沙箱实现,如 AgentBaySandbox -``` +import com.google.gson.Gson; +import io.agentscope.runtime.sandbox.box.BrowserSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -* **AgentBay沙箱(AgentBay Sandbox)**:基于 AgentBay 云服务的沙箱实现,支持多种镜像类型(Linux、Windows、Browser、CodeSpace、Mobile等) +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); -```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", AgentBaySandbox.class)) { - System.out.println(sandbox.listTools()); - if (sandbox instanceof AgentBaySandbox agentBaySandbox) { - String pythonResult = agentBaySandbox.runIpythonCell("print('Hello from the sandbox!')"); - System.out.println("Sandbox execution result: " + pythonResult); - String shellResult = agentBaySandbox.runShellCommand("echo Hello, World!"); - System.out.println("Shell command result: " + shellResult); +// 连接沙箱(沙箱会在执行后自动删除) + try (BrowserSandbox browserSandbox = new BrowserSandbox(sandboxService, "userId", "sessionId")) { + Gson gson = new Gson(); + String tools = gson.toJson(browserSandbox.listTools("")); + System.out.println("Available tools: "); + System.out.println(tools); + + String desktopUrl = browserSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); + String navigateResult = browserSandbox.navigate("https://cn.bing.com"); + System.out.println("Navigate Result: " + navigateResult); + } + catch (Exception e) { + e.printStackTrace(); + } } } ``` -**AgentBay 沙箱特性:** +* **移动端沙箱(Mobile Sandbox)**: 基于 Android 模拟器的沙箱,可进行移动端操作,如点击、滑动、输入文本和截屏等。 -* 无需本地 Docker,完全基于云服务 -* 支持多种环境类型 -* 自动管理会话生命周期 -* 通过 API 直接与云服务通信 + Mobile Sandbox -> 更多沙箱类型正在开发中,敬请期待! + - **运行环境要求** -### 向沙箱添加MCP服务器 + - **Linux 主机**: + 该沙箱在 Linux 主机上运行时,需要内核加载 `binder` 和 `ashmem` 模块。如果缺失,请在主机上执行以下命令来安装和加载所需模块: -MCP(模型上下文协议)是一个标准化协议,使AI应用程序能够安全地连接到外部数据源和工具。通过将MCP服务器集成到您的沙箱中,您可以在不影响安全性的情况下使用专门的工具和服务扩展沙箱的功能。 + ```bash + # 1. 安装额外的内核模块 + sudo apt update && sudo apt install -y linux-modules-extra-`uname -r` + + # 2. 加载模块并创建设备节点 + sudo modprobe binder_linux devices="binder,hwbinder,vndbinder" + sudo modprobe ashmem_linux + ``` -沙箱支持通过`add_mcp_servers`方法集成MCP服务器。添加后,您可以使用`list_tools`发现可用工具并使用`call_tool`执行它们。 + - **架构兼容性**: + 在 ARM64/aarch64 架构(如 Apple M 系列芯片)上运行时,可能会遇到兼容性或性能问题,建议在 x86_64 架构的主机上运行。 ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)) { -String mcpServerConfig = """ - { - "mcpServers": { - "time": { - "command": "uvx", - "args": [ - "mcp-server-time", - "--local-timezone=America/New_York" - ] - } - } - } - """; - -Gson gson = new Gson(); -Type mcpServerType = new TypeToken>() { -}.getType(); -Map serverConfigMap = gson.fromJson(mcpServerConfig, mcpServerType); - -// 将MCP服务器添加到沙箱 - sandbox.addMcpServers(serverConfigMap); +import io.agentscope.runtime.sandbox.box.MobileSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -// 列出所有可用工具(现在包括MCP工具) -String tools = gson.toJson(sandbox.listTools("")); - System.out.println("Available tools: "); - System.out.println(tools); +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); -// 使用MCP服务器提供的时间工具 -String result = sandbox.callTool("get_current_time", Map.of("timezone", "America/New_York")); - System.out.println("Tool call result: "); - System.out.println(result); + try (MobileSandbox mobileSandbox = new MobileSandbox(sandboxService, "userId", "sessionId")) { + System.out.println("列举所有工具"); + System.out.println(mobileSandbox.listTools("")); + + System.out.println("获取屏幕分辨率:"); + System.out.println(mobileSandbox.mobileGetScreenResolution()); + + System.out.println("在坐标(500,1000)处点击"); + System.out.println(mobileSandbox.mobileTap(500,1000)); + + System.out.println("输入文本:"); + System.out.println(mobileSandbox.mobileInputText("来自 AgentScope 的问候!")); + + System.out.println("发送 Home 按键请求:"); + System.out.println(mobileSandbox.mobileKeyEvent(3)); + + System.out.println("截取屏幕:"); + String screenshotResult = mobileSandbox.mobileGetScreenshot(); + System.out.println(screenshotResult); + } + catch (Exception e) { + e.printStackTrace(); + } + } } ``` -### 连接到远程沙箱 - -> 沙箱远程部署特别适用于: -> * 将计算密集型任务分离到专用服务器 -> * 多个客户端共享同一沙箱环境 -> * 在资源受限的本地机器上开发,同时在高性能服务器上执行 -> * K8s 集群部署沙盒服务 -> -> 有关sandbox-server的更高级用法,请参阅[工具沙箱高级用法](sandbox_advanced.md)了解详细说明。 +* **TrainingSandbox**:训练评估沙箱,详情请参考:[训练用沙箱](training_sandbox.md)。 -您可以在本地机器或不同机器上启动沙箱服务器,以便于远程访问。您可以先启动一个runtime,作为远程沙箱管理器 +```java +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; -要连接到远程沙箱服务,只需要在构建managerConfig的时候添加远程runtime的实际启动地址,其余操作和本地沙箱相同,在进行沙箱管理和工具调用的时候会自动将操作转发到远程runtime处理: +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); -```java -ManagerConfig managerConfig = ManagerConfig.builder() - .baseUrl("http://remote-host:port") - .build(); +// 连接沙箱(沙箱会在执行后自动删除) + try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} ``` -## 沙箱服务 +* **云沙箱(Cloud Sandbox)**:基于云服务的沙箱环境,无需本地 Docker 容器。`CloudSandbox` 是云沙箱的基类,提供了云沙箱的统一接口 -### 使用沙箱服务管理沙箱 +```java +// CloudSandbox 是抽象基类,通常不直接使用 +// 请使用具体的云沙箱实现,如 AgentBaySandbox +``` -`SandboxService` 提供了统一的沙箱管理接口,支持通过 `session_id` 和 `user_id` 来管理不同用户会话的沙箱环境。使用 `SandboxService` 可以让您更好地控制沙箱的生命周期,并实现沙箱的复用。 +* **AgentBay沙箱(AgentBay Sandbox)**:基于 AgentBay 云服务的沙箱实现,支持多种镜像类型(Linux、Windows、Browser、CodeSpace、Mobile等) ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.box.AgentBaySandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { // 创建并启动沙箱服务 - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .agentBayApiKey(System.getenv("AGENTBAY_API_KEY")) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - try { -// 连接到沙箱,指定需要的沙箱类型 - Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class); - if(sandbox instanceof BaseSandbox baseSandbox) { -// 直接在沙箱实例上调用工具方法 - String pythonResult = baseSandbox.runIpythonCell("a=1"); - System.out.println("Sandbox execution result: " + pythonResult); - } -// 使用相同的 session_id 和 user_id 会复用同一个沙箱实例 - sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class); - if(sandbox instanceof BaseSandbox baseSandbox) { -// 变量 a 仍然存在,因为复用了同一个沙箱 - String pythonResult = baseSandbox.runIpythonCell("print(a)"); - System.out.println("Sandbox execution result: " + pythonResult); - } -// 停止沙箱服务 - sandbox.close(); - } - catch (Exception e) { - e.printStackTrace(); +// 连接沙箱(沙箱会在执行后自动删除) + try (AgentBaySandbox agentBaySandbox = new AgentBaySandbox(sandboxService, "user", "session", "linux_latest")) { + System.out.println(agentBaySandbox.listTools()); + String pythonResult = agentBaySandbox.runIpythonCell("print('Hello from the sandbox!')"); + System.out.println("Sandbox execution result: " + pythonResult); + String shellResult = agentBaySandbox.runShellCommand("echo Hello, World!"); + System.out.println("Shell command result: " + shellResult); } } } ``` -### 使用沙箱服务添加MCP服务器 +**AgentBay 沙箱特性:** + +* 无需本地 Docker,完全基于云服务 +* 支持多种环境类型 +* 自动管理会话生命周期 +* 通过 API 直接与云服务通信 + +> 更多沙箱类型正在开发中,敬请期待! + +### 向沙箱添加MCP服务器 + +MCP(模型上下文协议)是一个标准化协议,使AI应用程序能够安全地连接到外部数据源和工具。通过将MCP服务器集成到您的沙箱中,您可以在不影响安全性的情况下使用专门的工具和服务扩展沙箱的功能。 + +沙箱支持通过`add_mcp_servers`方法集成MCP服务器。添加后,您可以使用`list_tools`发现可用工具并使用`call_tool`执行它们。 ```java import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import java.lang.reflect.Type; import java.util.Map; public class Main { public static void main(String[] args) { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class)) { +// 连接沙箱(沙箱会在执行后自动删除) + try (Sandbox sandbox = new BaseSandbox(sandboxService, "userID", "sessionID")) { String mcpServerConfig = """ { "mcpServers": { @@ -455,59 +525,79 @@ public class Main { }.getType(); Map serverConfigMap = gson.fromJson(mcpServerConfig, mcpServerType); -// 将MCP服务器添加到沙箱 +// 将MCP服务器添加到沙箱 sandbox.addMcpServers(serverConfigMap); -// 列出所有可用工具(现在包括MCP工具) +// 列出所有可用工具(现在包括MCP工具) String tools = gson.toJson(sandbox.listTools("")); System.out.println("Available tools: "); System.out.println(tools); -// 使用MCP服务器提供的时间工具 +// 使用MCP服务器提供的时间工具 String result = sandbox.callTool("get_current_time", Map.of("timezone", "America/New_York")); System.out.println("Tool call result: "); System.out.println(result); - } catch (Exception e) { - e.printStackTrace(); } } } ``` -### 使用沙箱服务连接远程沙箱 +### 连接到远程沙箱 + +> 沙箱远程部署特别适用于: +> * 将计算密集型任务分离到专用服务器 +> * 多个客户端共享同一沙箱环境 +> * 在资源受限的本地机器上开发,同时在高性能服务器上执行 +> * K8s 集群部署沙盒服务 +> +> 有关sandbox-server的更高级用法,请参阅[工具沙箱高级用法](sandbox_advanced.md)了解详细说明。 + +您可以在本地机器或不同机器上启动沙箱服务器,以便于远程访问。您可以先启动一个runtime,作为远程沙箱管理器 + +要连接到远程沙箱服务,只需要在构建managerConfig的时候添加远程runtime的实际启动地址,其余操作和本地沙箱相同,在进行沙箱管理和工具调用的时候会自动将操作转发到远程runtime处理: + +```java +ManagerConfig managerConfig = ManagerConfig.builder() + .baseUrl("http://remote-host:port") + .build(); +``` + +## 沙箱服务 + +### 使用沙箱服务管理沙箱 + +`SandboxService` 提供了统一的沙箱管理接口,支持通过 `session_id` 和 `user_id` 来管理不同用户会话的沙箱环境。使用 `SandboxService` 可以让您更好地控制沙箱的生命周期,并实现沙箱的复用。 ```java -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { // 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .baseUrl("http://remote-host:port") + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); try { // 连接到沙箱,指定需要的沙箱类型 - Sandbox sandbox = sandboxService.connect("sessionId", "userId", BaseSandbox.class); - if(sandbox instanceof BaseSandbox baseSandbox) { -// 直接在沙箱实例上调用工具方法 - String pythonResult = baseSandbox.runIpythonCell("a=1"); - System.out.println("Sandbox execution result: " + pythonResult); - } + BaseSandbox baseSandbox = new BaseSandbox(sandboxService, "userId", "sessionId"); +// 直接在沙箱实例上调用工具方法 + String pythonResult = baseSandbox.runIpythonCell("a=1"); + System.out.println("Sandbox execution result: " + pythonResult); +// 使用相同的 session_id 和 user_id 会复用同一个沙箱实例 + baseSandbox = new BaseSandbox(sandboxService, "userId", "sessionId"); + pythonResult = baseSandbox.runIpythonCell("print(a)"); + System.out.println("Sandbox execution result: " + pythonResult); // 停止沙箱服务 - sandbox.close(); - } - catch (Exception e) { + baseSandbox.close(); + } catch (Exception e) { e.printStackTrace(); } } diff --git a/cookbook/zh/sandbox/training_sandbox.md b/cookbook/zh/sandbox/training_sandbox.md index 38024dae..f7409605 100644 --- a/cookbook/zh/sandbox/training_sandbox.md +++ b/cookbook/zh/sandbox/training_sandbox.md @@ -34,39 +34,28 @@ docker pull agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtim 您可以通过调用`getEnvProfile`来验证一切设置是否正确,如果正确将返回数据集ID: -```python -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; - -import com.google.gson.Gson; +```java +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { // 创建并启动沙箱服务 - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); // 连接沙箱(沙箱会在执行后自动删除) - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ - String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); - System.out.println("Profile List: " + profileList); - } else { - System.err.println("Failed to connect to TrainingSandbox."); - } - } - catch (Exception e) { + try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); + } catch (Exception e) { e.printStackTrace(); } } @@ -96,13 +85,9 @@ docker build -f src/agentscope_runtime/sandbox/box/training_box/environments/app 例如,我们可以使用 `getEnvProfile` 方法获取训练ID列表。 ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ - String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); - System.out.println("Profile List: " + profileList); - } else { - System.err.println("Failed to connect to TrainingSandbox."); - } +try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld", "train", null); + System.out.println("Profile List: " + profileList); } ``` @@ -114,25 +99,48 @@ try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSan 中 ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", APPWorldSandbox.class)){ - if(sandbox instanceof APPWorldSandbox appWorldSandbox){ - String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); - System.out.println("Profile List: " + profileList); - - Gson gson = new Gson(); - Type listType = new TypeToken>(){}.getType(); - List list = gson.fromJson(profileList, listType); - String initResponse = appWorldSandbox.createInstance("appworld", list.get(0)); - Type instanceType = new TypeToken>(){}.getType(); - Map instance = gson.fromJson(initResponse, instanceType); - String instanceInfo = instance.get("info").toString(); - Type infoType = new TypeToken>(){}.getType(); - Map infoMap = gson.fromJson(instanceInfo, infoType); - String instanceId = (String) infoMap.get("instance_id"); - String query = instance.get("state").toString(); - System.out.println("Created instance " + instanceId + " with query: " + query); - } else { - System.err.println("Failed to connect to TrainingSandbox."); +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + +// 连接沙箱(沙箱会在执行后自动删除) + try (APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "userId", "sessionId")) { + String profileList = appWorldSandbox.getEnvProfile("appworld","train", null); + System.out.println("Profile List: " + profileList); + + Gson gson = new Gson(); + Type listType = new TypeToken>(){}.getType(); + List list = gson.fromJson(profileList, listType); + String initResponse = appWorldSandbox.createInstance("appworld", list.get(0)); + Type instanceType = new TypeToken>(){}.getType(); + Map instance = gson.fromJson(initResponse, instanceType); + String instanceInfo = instance.get("info").toString(); + Type infoType = new TypeToken>(){}.getType(); + Map infoMap = gson.fromJson(instanceInfo, infoType); + String instanceId = (String) infoMap.get("instance_id"); + String query = instance.get("state").toString(); + System.out.println("Created instance " + instanceId + " with query: " + query); + } catch (Exception e) { + e.printStackTrace(); + } } } ``` @@ -155,7 +163,7 @@ System.out.println("Step Result: " + result); 使用`evaluate`方法,并评测某个实例的状态,并获取`Reward`。不同的数据集可能含有额外的测评参数,通过`params`传入。 -```python +```java String score = appWorldSandbox.evaluate(instanceId, Map.of(), Map.of("sparse", true)); System.out.println("Evaluation Score: " + score); ``` @@ -191,8 +199,6 @@ docker pull agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtim (可选) 建立自己的Docker镜像AgentScope Runtime Python 根目录运行以下代码: - - ```bash docker build -f src/agentscope_runtime/sandbox/box/training_box/environments/bfcl/Dockerfile -t agentscope/runtime-sandbox-bfcl:latest . ``` @@ -206,25 +212,49 @@ BFCL 有多个子数据库 *all, all_scoring, multi_turn, single_turn, live, non ```java -try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", BFCLSandbox.class)){ - if(sandbox instanceof BFCLSandbox bfclSandbox){ - String profileList = bfclSandbox.getEnvProfile("bfcl"); - System.out.println("Connected to BFCLSandbox. Profile List: " + profileList); - - Gson gson = new Gson(); - Type listType = new TypeToken>(){}.getType(); - List list = gson.fromJson(profileList, listType); - String initResponse = bfclSandbox.createInstance("bfcl", list.get(0)); - Type instanceType = new TypeToken>(){}.getType(); - Map instance = gson.fromJson(initResponse, instanceType); - String instanceInfo = instance.get("info").toString(); - Type infoType = new TypeToken>(){}.getType(); - Map infoMap = gson.fromJson(instanceInfo, infoType); - String instanceId = (String) infoMap.get("instance_id"); - String query = instance.get("state").toString(); - System.out.println("Created instance " + instanceId + " with query: " + query); - } else { - System.err.println("Failed to connect to TrainingSandbox."); +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.agentscope.runtime.sandbox.box.APPWorldSandbox; +import io.agentscope.runtime.sandbox.box.BFCLSandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +public class Main { + public static void main(String[] args) { +// 创建并启动沙箱服务 + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + +// 连接沙箱(沙箱会在执行后自动删除) + try (BFCLSandbox bfclSandbox = new BFCLSandbox(sandboxService, "userId", "sessionId")) { + String profileList = bfclSandbox.getEnvProfile("bfcl"); + System.out.println("Connected to BFCLSandbox. Profile List: " + profileList); + + Gson gson = new Gson(); + Type listType = new TypeToken>(){}.getType(); + List list = gson.fromJson(profileList, listType); + String initResponse = bfclSandbox.createInstance("bfcl", list.get(0)); + Type instanceType = new TypeToken>(){}.getType(); + Map instance = gson.fromJson(initResponse, instanceType); + String instanceInfo = instance.get("info").toString(); + Type infoType = new TypeToken>(){}.getType(); + Map infoMap = gson.fromJson(instanceInfo, infoType); + String instanceId = (String) infoMap.get("instance_id"); + String query = instance.get("state").toString(); + System.out.println("Created instance " + instanceId + " with query: " + query); + } catch (Exception e) { + e.printStackTrace(); + } } } ``` @@ -248,7 +278,7 @@ List> assistantMessages = List.of( -```python +```java for (int i = 1; i <= assistantMessages.size(); ++i) { Map msg = assistantMessages.get(i); String response = bfclSandbox.step(instanceId, msg, null); @@ -261,7 +291,7 @@ for (int i = 1; i <= assistantMessages.size(); ++i) { ``` #### 评估实例 -```python +```java String score = bfclSandbox.evaluate(instanceId, Map.of("sparse", true), null); System.out.println("[RESULT] sparse_score = " + score); ``` diff --git a/cookbook/zh/service/sandbox.md b/cookbook/zh/service/sandbox.md index 43d96a4c..7f5944d4 100644 --- a/cookbook/zh/service/sandbox.md +++ b/cookbook/zh/service/sandbox.md @@ -15,7 +15,7 @@ 沙箱服务在不同实现中,差异主要体现在: **运行模式**(嵌入式/远程)、**支持的类型**、**管理方式**以及**可扩展性**。 -> 在业务代码中,不建议直接编写沙箱服务与 `SandboxManager` 的底层管理逻辑。 +> 在业务代码中,不建议直接编写沙箱服务与 `SandboxService` 的底层管理逻辑。 > > 更推荐 **使用AgentScope Runtime Java封装好的沙箱方法** 绑定到智能体框架的工具模块**: > - 屏蔽底层沙箱 API 细节 @@ -29,28 +29,25 @@ ```java import io.agentscope.core.tool.Toolkit; import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BrowserSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { -// 1. 启动服务(通常由 Runner/Engine 托管) - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); +// 1. 启动服务 + BaseClientStarter clientStarter = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientStarter) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); // 2. 连接或创建沙箱(此处创建浏览器类型) - Sandbox sandbox = sandboxService.connect("TestSession", "User1", BrowserSandbox.class); + Sandbox sandbox = new BrowserSandbox(sandboxService, "user", "session"); // 3. 获取工具方法并注册到 Agent 的 Toolkit Toolkit toolkit = new Toolkit(); @@ -73,23 +70,23 @@ public class Main { ### 2. **远程 API 模式** -- **特点**:通过沙箱管理 API(`SandboxManager`)连接远程沙箱实例。 +- **特点**:通过沙箱管理 API(`SandboxService`)连接远程沙箱实例。 - **配置**:`baseUrl="http://host:port"`, `bearerToken="..."` - **优点**:可跨进程/跨机器共享环境,支持分布式扩展。 - **缺点**:需要部署和运维远程沙箱管理服务。 ### 支持的沙箱类型 -| 类型值 | 功能描述 | 常见用途示例 | -| ------------ | ---------------------------- | ------------------------------------------- | -| `DUMMY` | 空实现/占位沙箱 | 测试流程,模拟沙箱接口但不执行实际操作 | -| `BASE` | 基础沙箱环境 | 通用工具运行环境 | -| `BROWSER` | 浏览器沙箱 | 网页导航、截图、数据抓取 | -| `FILESYSTEM` | 文件系统沙箱 | 在安全隔离的文件系统中读写文件 | -| `GUI` | 图形界面沙箱 | 与 GUI 应用交互(点击、输入、截屏) | -| `APPWORLD` | 应用世界仿真沙箱 | 在虚拟环境中模拟跨应用交互 | -| `BFCL` | BFCL(特定业务领域执行环境) | 运行业务流程脚本(具体取决于实现) | -| `AGENTBAY` | AgentBay 会话型沙箱 | 专用于多 Agent 协作或复杂任务编排的持久环境 | +| 类型值 | 功能描述 | 常见用途示例 | +|--------------|------------------|---------------------------| +| `BASE` | 基础沙箱环境 | 通用工具运行环境 | +| `BROWSER` | 浏览器沙箱 | 网页导航、截图、数据抓取 | +| `FILESYSTEM` | 文件系统沙箱 | 在安全隔离的文件系统中读写文件 | +| `GUI` | 图形界面沙箱 | 与 GUI 应用交互(点击、输入、截屏) | +| `APPWORLD` | 应用世界仿真沙箱 | 在虚拟环境中模拟跨应用交互 | +| `BFCL` | BFCL(特定业务领域执行环境) | 运行业务流程脚本(具体取决于实现) | +| `AGENTBAY` | AgentBay 会话型沙箱 | 专用于多 Agent 协作或复杂任务编排的持久环境 | +| `MOBILE` | 移动沙箱 | 模拟移动设备操作 | ## 切换运行模式示例 @@ -99,9 +96,8 @@ public class Main { // 本地模式(默认使用本地 Docker) ManagerConfig managerConfig = ManagerConfig.builder() .build(); -SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) -); +SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); Sandbox sandbox = sandboxService.connect("DevSession", "User1", BrowserSandbox.class); @@ -112,15 +108,13 @@ Sandbox sandbox = sandboxService.connect("DevSession", "User1", BrowserSandbox.c ```java // 本地模式(默认使用本地 Docker) ManagerConfig managerConfig = ManagerConfig.builder() - .baseUrl("https://sandbox-manager.com") - .bearerToken("YOUR_AUTH_TOKEN") - .build(); -SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) -); + .baseUrl("https://sandbox-manager.com") + .bearerToken("YOUR_AUTH_TOKEN") + .build(); +SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); -Sandbox sandbox = sandboxService.connect("ProdSession", "UserABC", BrowserSandbox.class); +Sandbox sandbox = new BrowserSandbox(sandboxService, "ProdSession", "UserABC"); ``` ### 释放环境 @@ -128,8 +122,11 @@ Sandbox sandbox = sandboxService.connect("ProdSession", "UserABC", BrowserSandbo 会话结束时显式释放资源: ```java -// 通过容器名称直接释放 -sandboxService.getManagerApi().release("container_name"); +// 通过容器 ID 直接释放 +sandboxService.release("container_id"); + +// 或通过沙箱实例释放 +sandbox.release(); ``` ## 选型建议 @@ -138,7 +135,7 @@ sandboxService.getManagerApi().release("container_name"); - 嵌入式模式 (`baseUrl=null`) - 选用 `BROWSER`/`BASE` 类型按需创建 - 生产环境 / 多用户分布式: - - 远程 API 模式(需部署 `SandboxManager` 服务) + - 远程 API 模式(需使用 `web` 模块部署 `SandboxService` 服务) - 考虑集群和认证机制(`bearerToken`) - 安全或隔离要求高的场景: - 为不同用户会话创建独立沙箱 diff --git a/cookbook/zh/service/service.md b/cookbook/zh/service/service.md index 9ffbdda8..411d1c97 100644 --- a/cookbook/zh/service/service.md +++ b/cookbook/zh/service/service.md @@ -99,26 +99,23 @@ public class Main { ```java import io.agentscope.core.tool.Toolkit; import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BrowserSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); + BaseClientStarter clientStarter = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientStarter) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - Sandbox sandbox = sandboxService.connect("TestSession", "User1", BrowserSandbox.class); + Sandbox sandbox = new BrowserSandbox(sandboxService, "user_1", "session_1"); Toolkit toolkit = new Toolkit(); toolkit.registerTool(ToolkitInit.BrowserNavigateTool(sandbox)); diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/sandbox/SandboxService.java b/core/src/main/java/io/agentscope/runtime/engine/services/sandbox/SandboxService.java deleted file mode 100644 index 43e79ebc..00000000 --- a/core/src/main/java/io/agentscope/runtime/engine/services/sandbox/SandboxService.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.agentscope.runtime.engine.services.sandbox; - -import io.agentscope.runtime.engine.services.ServiceWithLifecycleManager; -import io.agentscope.runtime.sandbox.box.AgentBaySandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; - -/** - * Service for managing sandbox environments. - * - *

This service provides functionality to connect to sandbox environments, - * create new environments, and release them. It manages the lifecycle of - * sandbox instances associated with user sessions. - * - *

Usage Example: - *

{@code
- * SandboxService sandboxService = new SandboxService("http://localhost:8000", "token");
- * sandboxService.start();
- *
- * // Connect to sandbox for a session
- * Sandbox sandbox = sandboxService.connect("session_123", "user_456", BaseSandbox.class);
- *
- * // Release sandbox
- * sandboxService.release("session_123", "user_456");
- * }
- */ -public class SandboxService extends ServiceWithLifecycleManager { - private static final Logger logger = LoggerFactory.getLogger(SandboxService.class); - - private SandboxManager managerApi; - private boolean health = false; - - public SandboxService(SandboxManager sandboxManager) { - this.managerApi = sandboxManager; - } - - public SandboxManager getManagerApi() { - return managerApi; - } - - @Override - public void start() { - if (managerApi != null) { - managerApi.start(); - } - health = true; - } - - @Override - public void stop() { - health = false; - if (managerApi != null) { - managerApi.close(); - managerApi = null; - } - } - - @Override - public boolean health() { - return health; - } - - /** - * Connect to sandbox environment for a session. - * - * @param sessionId The session ID - * @param userId Optional user ID - * @param sandboxType sandbox type to connect to - * @return List of sandbox instances - */ - public Sandbox connect( - String sessionId, - String userId, - Class sandboxType) { - try { - return sandboxType.getConstructor( - SandboxManager.class, - String.class, - String.class - ).newInstance(managerApi, userId, sessionId); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public Sandbox connect( - String sessionId, - String userId, - Class sandboxType, - String imageId) { - try { - if(sandboxType!= AgentBaySandbox.class){ - logger.warn("The imageId parameter is only applicable to AgentBaySandbox. Ignoring imageId for other sandbox types."); - return sandboxType.getConstructor( - SandboxManager.class, - String.class, - String.class - ).newInstance(managerApi, userId, sessionId); - } - return sandboxType.getConstructor( - SandboxManager.class, - String.class, - String.class, - String.class - ).newInstance(managerApi, userId, sessionId, imageId); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public Sandbox connect( - String sessionId, - String userId, - Class sandboxType, - String imageId, - Map labels) { - try { - if(sandboxType!= AgentBaySandbox.class){ - logger.warn("The imageId parameter is only applicable to AgentBaySandbox. Ignoring imageId for other sandbox types."); - return sandboxType.getConstructor( - SandboxManager.class, - String.class, - String.class - ).newInstance(managerApi, userId, sessionId); - } - return sandboxType.getConstructor( - SandboxManager.class, - String.class, - String.class, - String.class, - Map.class - ).newInstance(managerApi, userId, sessionId, imageId, labels); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/APPWorldSandbox.java b/core/src/main/java/io/agentscope/runtime/sandbox/box/APPWorldSandbox.java deleted file mode 100644 index 7d843623..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/APPWorldSandbox.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.box; - -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; - - -@RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-appworld:latest", - sandboxType = SandboxType.APPWORLD, - runtimeConfig = {"shm_size=5.06gb"}, - securityLevel = "medium", - timeout = 30, - description = "appworld Sandbox" -) -public class APPWorldSandbox extends TrainingSandbox { - - public APPWorldSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); - } - - public APPWorldSandbox( - SandboxManager managerApi, - String userId, - String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.APPWORLD, timeout); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/DummySandbox.java b/core/src/main/java/io/agentscope/runtime/sandbox/box/DummySandbox.java deleted file mode 100644 index 5ff948a4..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/DummySandbox.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.box; - -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; - -public class DummySandbox implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(DummySandbox.class); - - private final String sandboxId; - private final String userId; - private final String sessionId; - private final SandboxType sandboxType; - private boolean closed = false; - - public DummySandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); - } - - public DummySandbox( - SandboxManager managerApi, - String userId, - String sessionId, - int timeout) { - this.userId = userId; - this.sessionId = sessionId; - this.sandboxType = SandboxType.BASE; - - // For dummy sandbox, we just use a fixed ID - this.sandboxId = "dummy-sandbox-" + userId + "-" + sessionId; - - logger.info("DummySandbox initialized: {} (This is a dummy sandbox, no real container created)", this.sandboxId); - } - - public String getSandboxId() { - return sandboxId; - } - - public String getUserId() { - return userId; - } - - public String getSessionId() { - return sessionId; - } - - public SandboxType getSandboxType() { - return sandboxType; - } - - public ContainerModel getInfo() { - logger.info("DummySandbox.getInfo() called - returning dummy info"); - ContainerModel model = new ContainerModel(); - model.setContainerName(sandboxId); - model.setSessionId(sessionId); - return model; - } - - public Map listTools(String toolType) { - logger.info("DummySandbox.listTools() called - returning empty map"); - return new java.util.HashMap<>(); - } - - public String callTool(String name, Map arguments) { - logger.info("DummySandbox.callTool() called with name={} - returning dummy response", name); - return "{\"status\": \"success\", \"message\": \"Dummy sandbox call\"}"; - } - - @Override - public void close() { - if (closed) { - return; - } - closed = true; - logger.info("DummySandbox.close() called - no-op"); - } - - public void release() { - logger.info("DummySandbox.release() called - no-op"); - close(); - } - - public boolean isClosed() { - return closed; - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/Sandbox.java b/core/src/main/java/io/agentscope/runtime/sandbox/box/Sandbox.java deleted file mode 100644 index e361f684..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/Sandbox.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.box; - -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; - -public abstract class Sandbox implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(Sandbox.class); - - protected final SandboxManager managerApi; - protected String sandboxId; - protected final String userId; - protected final String sessionId; - protected final SandboxType sandboxType; - protected final int timeout; - protected final boolean autoRelease; - protected boolean closed = false; - - public Sandbox( - SandboxManager managerApi, - String userId, - String sessionId, - SandboxType sandboxType, - int timeout) { - this(managerApi, userId, sessionId, sandboxType, timeout, true); - } - - public Sandbox( - SandboxManager managerApi, - String userId, - String sessionId, - SandboxType sandboxType, - int timeout, - boolean autoRelease) { - this.managerApi = managerApi; - this.userId = userId; - this.sessionId = sessionId; - this.sandboxType = sandboxType; - this.timeout = timeout; - this.autoRelease = autoRelease; - - if(sandboxType != SandboxType.AGENTBAY){ - try { - ContainerModel containerModel = managerApi.createFromPool(sandboxType, userId, sessionId); - if (containerModel == null) { - throw new RuntimeException( - "No sandbox available. Please check if sandbox images exist." - ); - } - this.sandboxId = containerModel.getContainerName(); - logger.info("Sandbox initialized: {} (type={}, user={}, session={}, autoRelease={})", this.sandboxId, sandboxType, userId, sessionId, autoRelease); - } catch (Exception e) { - logger.error("Failed to initialize sandbox: {}", e.getMessage()); - throw new RuntimeException("Failed to initialize sandbox", e); - } - } - } - - public String getSandboxId() { - return sandboxId; - } - - public String getUserId() { - return userId; - } - - public String getSessionId() { - return sessionId; - } - - public SandboxType getSandboxType() { - return sandboxType; - } - - public ContainerModel getInfo() { - return managerApi.getInfo(sandboxId); - } - - public Map listTools() { - return listTools(null); - } - - public Map listTools(String toolType) { - return managerApi.listTools(sandboxId, userId, sessionId, toolType); - } - - public String callTool(String name, Map arguments) { - return managerApi.callTool(sandboxId, userId, sessionId, name, arguments); - } - - public Map addMcpServers(Map serverConfigs) { - return addMcpServers(serverConfigs, false); - } - - public Map addMcpServers(Map serverConfigs, boolean overwrite){ - return managerApi.addMcpServers(sandboxId, userId, sessionId, serverConfigs, overwrite); - } - - @Override - public void close() { - if (closed) { - return; - } - - closed = true; - - try { - if (autoRelease) { - logger.info("Auto-releasing sandbox: {}", sandboxId); - managerApi.releaseSandbox(sandboxType, userId, sessionId); - } else { - logger.info("Sandbox closed (not released, can be reused): {}", sandboxId); - } - } catch (Exception e) { - logger.error("Failed to cleanup sandbox: {}", e.getMessage()); - } - } - - /** - * Manually release sandbox resources - * Forces release of underlying container regardless of autoRelease setting - */ - public void release() { - try { - logger.info("Manually releasing sandbox: {}", sandboxId); - managerApi.releaseSandbox(sandboxType, userId, sessionId); - closed = true; - } catch (Exception e) { - logger.error("Failed to release sandbox: {}", e.getMessage()); - throw new RuntimeException("Failed to release sandbox", e); - } - } - - public boolean isClosed() { - return closed; - } -} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/WebShopSandbox.java b/core/src/main/java/io/agentscope/runtime/sandbox/box/WebShopSandbox.java deleted file mode 100644 index 21384cb1..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/WebShopSandbox.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.box; - -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; - -@RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-webshop:latest", - sandboxType = SandboxType.WEBSHOP, - runtimeConfig = {"shm_size=5.06gb"}, - securityLevel = "medium", - timeout = 30, - description = "webshop Sandbox" -) -public class WebShopSandbox extends TrainingSandbox { - - public WebShopSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); - } - - public WebShopSandbox( - SandboxManager managerApi, - String userId, - String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.WEBSHOP, timeout); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/SandboxManager.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/SandboxManager.java deleted file mode 100644 index 6038e4c1..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/SandboxManager.java +++ /dev/null @@ -1,1181 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager; - -import io.agentscope.runtime.sandbox.manager.client.*; -import io.agentscope.runtime.sandbox.manager.client.config.AgentRunClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.FcClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.collections.ContainerQueue; -import io.agentscope.runtime.sandbox.manager.collections.InMemoryContainerQueue; -import io.agentscope.runtime.sandbox.manager.collections.RedisContainerMapping; -import io.agentscope.runtime.sandbox.manager.collections.RedisContainerQueue; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.*; -import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; -import io.agentscope.runtime.sandbox.manager.registry.SandboxRegistryService; -import io.agentscope.runtime.sandbox.manager.remote.RemoteHttpClient; -import io.agentscope.runtime.sandbox.manager.remote.RemoteWrapper; -import io.agentscope.runtime.sandbox.manager.remote.RequestMethod; -import io.agentscope.runtime.sandbox.manager.util.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.file.Paths; -import java.util.*; - -/** - * Sandbox Manager for managing container lifecycle - */ -public class SandboxManager implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(SandboxManager.class); - - private final Map sandboxMap = new HashMap<>(); - private final ContainerManagerType containerManagerType; - String BROWSER_SESSION_ID = "123e4567-e89b-12d3-a456-426614174000"; - private final ManagerConfig managerConfig; - private BaseClient containerClient; - private final StorageManager storageManager; - private final int poolSize; - private ContainerQueue poolQueue; - private final PortManager portManager; - private RedisClientWrapper redisClient; - private RedisContainerMapping redisContainerMapping; - private final boolean redisEnabled; - private final RemoteHttpClient remoteHttpClient; - private AgentBayClient agentBayClient; - - public AgentBayClient getAgentBayClient() { - return agentBayClient; - } - - - public SandboxManager() { - this(null, null); - } - - public SandboxManager(String baseUrl, String bearerToken) { - this(new ManagerConfig.Builder().baseUrl(baseUrl).bearerToken(bearerToken).build()); - } - - public SandboxManager(ManagerConfig managerConfig) { - this(managerConfig, managerConfig.getBaseUrl(), managerConfig.getBearerToken()); - } - - public SandboxManager(ManagerConfig managerConfig, String baseUrl, String bearerToken) { - if (baseUrl != null && !baseUrl.isEmpty()) { - this.remoteHttpClient = new RemoteHttpClient(baseUrl, bearerToken); - logger.info("Initialized SandboxManager in remote mode with base URL: {}", baseUrl); - this.managerConfig = managerConfig != null ? managerConfig : new ManagerConfig.Builder().build(); - this.containerManagerType = ContainerManagerType.CLOUD; - this.storageManager = null; - this.poolSize = 0; - this.redisEnabled = false; - this.portManager = null; - this.poolQueue = null; - this.containerClient = null; - return; - } - - this.remoteHttpClient = null; - this.poolQueue = null; - this.managerConfig = managerConfig; - this.containerManagerType = managerConfig.getClientConfig().getClientType(); - this.storageManager = new StorageManager(managerConfig.getFileSystemConfig()); - this.poolSize = managerConfig.getPoolSize(); - this.redisEnabled = managerConfig.getRedisEnabled(); - this.portManager = new PortManager(managerConfig.getPortRange()); - - logger.info("Initializing SandboxManager with container manager: {}", this.containerManagerType); - logger.info("Container pool size: {}", this.poolSize); - logger.info("Redis enabled: {}", this.redisEnabled); - } - - public void start() { - if (this.redisEnabled) { - try { - RedisManagerConfig redisConfig = managerConfig.getRedisConfig(); - this.redisClient = new RedisClientWrapper(redisConfig); - - String pong = this.redisClient.ping(); - logger.info("Redis connection test: {}", pong); - - String mappingPrefix = managerConfig.getContainerPrefixKey() + "mapping"; - this.redisContainerMapping = new RedisContainerMapping(this.redisClient, mappingPrefix); - - String queueName = redisConfig.getRedisContainerPoolKey(); - this.poolQueue = new RedisContainerQueue(this.redisClient, queueName); - logger.info("Using Redis-backed container pool with queue: {}", queueName); - - logger.info("Redis client initialized successfully for container management"); - } catch (Exception e) { - logger.error("Failed to initialize Redis client: {}", e.getMessage()); - throw new RuntimeException("Failed to initialize Redis", e); - } - } else { - this.poolQueue = new InMemoryContainerQueue(); // Use in-memory queue - logger.info("Using in-memory container storage"); - } - - logger.info("Using container type: {}", this.containerManagerType); - - if (managerConfig.getAgentBayApiKey() != null) { - agentBayClient = new AgentBayClient(managerConfig.getAgentBayApiKey()); - } - - switch (this.containerManagerType) { - case DOCKER: - DockerClientConfig dockerClientConfig; - if (managerConfig.getClientConfig() instanceof DockerClientConfig existingConfig) { - dockerClientConfig = existingConfig; - } else { - dockerClientConfig = DockerClientConfig.builder().build(); - } - DockerClient dockerClient = new DockerClient(dockerClientConfig, portManager); - logger.info("Docker client created: {}", dockerClient); - - this.containerClient = dockerClient; - dockerClient.connectDocker(); - break; - case KUBERNETES: - KubernetesClient kubernetesClient; - if (managerConfig.getClientConfig() instanceof KubernetesClientConfig kubernetesClientConfig) { - kubernetesClient = new KubernetesClient(kubernetesClientConfig); - } else { - logger.warn("Provided clientConfig is not an instance of KubernetesClientConfig, using default configuration"); - kubernetesClient = new KubernetesClient(); - } - this.containerClient = kubernetesClient; - kubernetesClient.connect(); - break; - case AGENTRUN: - this.containerClient = getAgentRunClient(managerConfig); - containerClient.connect(); - break; - case FC: - this.containerClient = getFcClient(managerConfig); - containerClient.connect(); - case CLOUD: - break; - default: - throw new IllegalArgumentException("Unsupported container manager type: " + this.containerManagerType); - } - - if (this.poolSize > 0) { - initContainerPool(); - } - } - - private FcClient getFcClient(ManagerConfig managerConfig) { - FcClient fcClient; - try { - if (managerConfig.getClientConfig() instanceof FcClientConfig fcClientConfig) { - fcClient = new FcClient(fcClientConfig); - } else { - throw new RuntimeException("Provided clientConfig is not an instance of FC config, using default configuration"); - } - } catch (Exception e) { - throw new RuntimeException("Failed to initialize FC client"); - } - return fcClient; - } - - private AgentRunClient getAgentRunClient(ManagerConfig managerConfig) { - AgentRunClient agentRunClient; - try { - if (managerConfig.getClientConfig() instanceof AgentRunClientConfig agentRunClientConfig) { - agentRunClient = new AgentRunClient(agentRunClientConfig); - } else { - throw new RuntimeException("Provided clientConfig is not an instance of AgentRun config, using default configuration"); - } - } catch (Exception e) { - throw new RuntimeException("Failed to initialize agentrun client"); - } - return agentRunClient; - } - - private void initContainerPool() { - logger.info("Initializing container pool with size: {}", poolSize); - - while (poolQueue.size() < poolSize) { - for (SandboxType type : managerConfig.getDefaultSandboxType()) { - if (type == SandboxType.AGENTBAY) { - logger.warn("Skipping AgentBay sandbox type for container pool initialization"); - continue; - } - try { - ContainerModel containerModel = createContainer(type); - - if (containerModel != null) { - if (poolQueue.size() < poolSize) { - poolQueue.enqueue(containerModel); - logger.info("Added container to pool: {} (pool size: {}/{})", containerModel.getContainerName(), poolQueue.size(), poolSize); - } else { - logger.info("Pool size limit reached, releasing container: {}", containerModel.getContainerName()); - releaseContainer(containerModel); - break; - } - } else { - logger.error("Failed to create container for pool"); - break; - } - } catch (Exception e) { - logger.error("Error initializing container pool: {}", e.getMessage()); - break; - } - } - } - logger.info("Container pool initialization complete. Pool size: {}", poolQueue.size()); - } - - public ContainerModel createFromPool(SandboxType sandboxType, String imageId, Map labels) { - int attempts = 0; - int maxAttempts = poolSize + 1; - - while (attempts < maxAttempts) { - attempts++; - - try { - ContainerModel newContainer = createContainer(sandboxType, null, null, null, imageId, labels); - if (newContainer != null) { - poolQueue.enqueue(newContainer); - } - - ContainerModel containerModel = poolQueue.dequeue(); - - if (containerModel == null) { - logger.warn("No container available in pool after {} attempts", attempts); - continue; - } - - logger.info("Retrieved container from pool: {}", containerModel.getContainerName()); - - String currentImage = SandboxRegistryService.getImageByType(sandboxType).orElse(containerModel.getVersion()); - - if (!currentImage.equals(containerModel.getVersion())) { - logger.warn("Container {} is outdated (has: {}, current: {}), releasing and trying next", - containerModel.getContainerName(), containerModel.getVersion(), currentImage); - releaseContainer(containerModel); - continue; - } - - if (!containerClient.inspectContainer(containerModel.getContainerId())) { - logger.warn("Container {} not found or has been removed externally, trying next", containerModel.getContainerId()); - continue; - } - - String status = containerClient.getContainerStatus(containerModel.getContainerId()); - if (!"running".equals(status)) { - logger.warn("Container {} is not running (status: {}), trying next", containerModel.getContainerId(), status); - releaseContainer(containerModel); - continue; - } - - logger.info("Successfully retrieved running container from pool: {}", containerModel.getContainerName()); - return containerModel; - - } catch (Exception e) { - logger.error("Error getting container from pool (attempt {}): {}", attempts, e.getMessage()); - } - } - - logger.warn("Failed to get container from pool after {} attempts, creating new container", maxAttempts); - return createContainer(sandboxType, null, null, null, imageId, labels); - } - - public ContainerModel createFromPool(SandboxType sandboxType, String userID, String sessionID) { - return createFromPool(sandboxType, userID, sessionID, null, null); - } - - @RemoteWrapper - public ContainerModel createFromPool(SandboxType sandboxType, String userID, String sessionID, String imageId, Map labels) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding createFromPool(with userID/sessionID) to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxType", sandboxType != null ? sandboxType.name() : null); - requestData.put("userID", userID); - requestData.put("sessionID", sessionID); - requestData.put("imageId", imageId); - requestData.put("labels", labels); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/createFromPool", - requestData, - "data" - ); - if (result instanceof Map) { - @SuppressWarnings("unchecked") - Map resultMap = (Map) result; - ContainerModel containerModel = ContainerModel.fromMap(resultMap); - poolQueue.enqueue(containerModel); - return containerModel; - } - return null; - } - - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType, imageId); - ContainerModel existingContainer = sandboxMap.get(key); - - if (existingContainer != null) { - logger.info("Reusing existing container: {} (userID: {}, sessionID: {})", - existingContainer.getContainerName(), userID, sessionID); - return existingContainer; - } - - if (redisEnabled && redisContainerMapping != null) { - existingContainer = redisContainerMapping.get(key); - if (existingContainer != null) { - sandboxMap.put(key, existingContainer); - logger.info("Retrieved container from Redis: {} (userID: {}, sessionID: {})", - existingContainer.getContainerName(), userID, sessionID); - return existingContainer; - } - } - - ContainerModel containerModel = createFromPool(sandboxType, imageId, labels); - - if (containerModel == null) { - logger.error("Failed to get container from pool"); - return null; - } - - sandboxMap.put(key, containerModel); - - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.put(key, containerModel); - logger.info("Stored pool container in Redis"); - } - - logger.info("Added pool container to sandbox map: {} (userID: {}, sessionID(key): {}, container sessionId: {})", - containerModel.getContainerName(), userID, sessionID, containerModel.getSessionId()); - - return containerModel; - } - - public ContainerModel createContainer(SandboxType sandboxType) { - return createContainer(sandboxType, null, null, null); - } - - public ContainerModel createContainer(SandboxType sandboxType, String mountDir, String storagePath, Map environment) { - return createContainer(sandboxType, mountDir, storagePath, environment, null, null); - } - - @RemoteWrapper - public ContainerModel createContainer(SandboxType sandboxType, String mountDir, String storagePath, Map environment, String imageId, Map labels) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding createContainer to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxType", sandboxType != null ? sandboxType.name() : null); - requestData.put("mountDir", mountDir); - requestData.put("storagePath", storagePath); - requestData.put("environment", environment); - requestData.put("imageId", imageId); - requestData.put("labels", labels); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/createContainer", - requestData, - "data" - ); - if (result instanceof Map) { - @SuppressWarnings("unchecked") - Map resultMap = (Map) result; - return ContainerModel.fromMap(resultMap); - } - return null; - } - - if (sandboxType == SandboxType.AGENTBAY) { - ContainerCreateResult createResult = agentBayClient.createContainer(imageId, labels); - - String containerId = createResult.getContainerId(); - if (containerId == null) { - logger.error("Container creation failed: containerId is null"); - return null; - } - List resultPorts = createResult.getPorts(); - String ip = createResult.getIp(); - String httpProtocol = createResult.getProtocol(); - - String firstPort = resultPorts != null && !resultPorts.isEmpty() ? resultPorts.get(0) : "80"; - - String baseHost = ip != null ? ip : "localhost"; - String accessPort = firstPort; - - String[] mappedPorts = resultPorts != null ? resultPorts.toArray(new String[0]) : new String[]{firstPort}; - - return ContainerModel.builder() - .sessionId(containerId) - .containerId(containerId) - .containerName(containerId) - .baseUrl(String.format("%s://%s:%s/fastapi", httpProtocol, baseHost, accessPort)) - .browserUrl(String.format("%s://%s:%s/steel-api/%s", httpProtocol, baseHost, accessPort, "")) - .frontBrowserWS(String.format("ws://%s:%s/steel-api/%s/v1/sessions/cast", baseHost, accessPort, "")) - .clientBrowserWS(String.format("ws://%s:%s/steel-api/%s/&sessionId=%s", baseHost, accessPort, "", BROWSER_SESSION_ID)) - .artifactsSIO(String.format("%s://%s:%s/v1", httpProtocol, baseHost, accessPort)) - .ports(mappedPorts) - .mountDir(mountDir) - .storagePath(storagePath) - .runtimeToken("") - .authToken("") - .version("agentbay-cloud") - .build(); - } - - if (environment == null) { - environment = new HashMap<>(); - } - - for (Map.Entry entry : environment.entrySet()) { - String value = entry.getValue(); - if (value == null) { - logger.warn("Environment variable {} has null value", entry.getKey()); - return null; - } - } - String workdir = "/workspace"; - String default_mount_dir = managerConfig.getFileSystemConfig().getMountDir(); - String[] portsArray = {"80/tcp"}; - List ports = Arrays.asList(portsArray); - String imageName = SandboxRegistryService.getImageByType(sandboxType).orElse("agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-base:latest"); - SandboxConfig sandboxConfig = SandboxRegistryService.getConfigByType(sandboxType).orElse(null); - if (sandboxConfig != null) { - logger.info("Using registered sandbox configuration: {}", sandboxConfig.getDescription()); - if (sandboxConfig.getEnvironment() != null && !sandboxConfig.getEnvironment().isEmpty()) { - Map mergedEnv = new HashMap<>(sandboxConfig.getEnvironment()); - mergedEnv.putAll(environment); - environment = mergedEnv; - } - } - - logger.info("Checking image: {}", imageName); - if (containerManagerType == ContainerManagerType.DOCKER) { - if (!containerClient.ensureImageAvailable(imageName)) { - logger.error("Can not get image: {}", imageName); - throw new RuntimeException("Pull image failed: " + imageName); - } - logger.info("Docker image is ready: {}", imageName); - } else if (containerManagerType == ContainerManagerType.KUBERNETES) { - logger.info("Kubernetes image is ready: {}", imageName); - } - String sessionId = RandomStringGenerator.generateRandomString(22); - String currentDir = System.getProperty("user.dir"); - if (mountDir == null || mountDir.isEmpty()) { - mountDir = currentDir + "/" + default_mount_dir + "/" + sessionId; - } - if (containerManagerType == ContainerManagerType.AGENTRUN || containerManagerType == ContainerManagerType.FC) { - mountDir = Paths.get(mountDir).toAbsolutePath().toString(); - } - java.io.File file = new java.io.File(mountDir); - if (!file.exists()) { - file.mkdirs(); - } - // Todo:Currently using global storage path if not provided, still need to wait for next movement of python version - if (storagePath == null) { - storagePath = managerConfig.getFileSystemConfig().getStorageFolderPath(); - } - if (!mountDir.isEmpty() && !storagePath.isEmpty() && containerManagerType != ContainerManagerType.AGENTRUN && containerManagerType != ContainerManagerType.FC) { - logger.info("Downloading from storage path: {} to mount dir: {}", storagePath, mountDir); - boolean downloadSuccess = storageManager.downloadFolder(storagePath, mountDir); - if (downloadSuccess) { - logger.info("Successfully downloaded files from storage"); - } else { - logger.warn("Failed to download files from storage, continuing with empty mount dir"); - } - } - String runtimeToken = RandomStringGenerator.generateRandomString(32); - environment.put("SECRET_TOKEN", runtimeToken); - List volumeBindings = new ArrayList<>(); - if (containerManagerType != ContainerManagerType.AGENTRUN && containerManagerType != ContainerManagerType.FC) { - volumeBindings.add(new VolumeBinding(mountDir, workdir, "rw")); - } - Map readonlyMounts = managerConfig.getFileSystemConfig().getReadonlyMounts(); - if (readonlyMounts != null && !readonlyMounts.isEmpty()) { - logger.info("Adding readonly mounts: {} mount(s)", readonlyMounts.size()); - for (Map.Entry entry : readonlyMounts.entrySet()) { - String hostPath = entry.getKey(); - String containerPath = entry.getValue(); - if (!java.nio.file.Paths.get(hostPath).isAbsolute()) { - hostPath = java.nio.file.Paths.get(hostPath).toAbsolutePath().toString(); - logger.info("Converting relative path to absolute: {}", hostPath); - } - java.io.File hostFile = new java.io.File(hostPath); - if (!hostFile.exists()) { - logger.warn("Readonly mount host path does not exist: {}, skipping", hostPath); - continue; - } - volumeBindings.add(new VolumeBinding(hostPath, containerPath, "ro")); - logger.info("Added readonly mount: {} -> {}", hostPath, containerPath); - } - } - Map nonCopyMounts = managerConfig.getFileSystemConfig().getNonCopyMount(); - if(nonCopyMounts != null && !nonCopyMounts.isEmpty()){ - logger.info("Adding non-copy mounts: {} mount(s)", nonCopyMounts.size()); - for (Map.Entry entry : nonCopyMounts.entrySet()) { - String hostPath = entry.getKey(); - String containerPath = entry.getValue(); - if (!java.nio.file.Paths.get(hostPath).isAbsolute()) { - hostPath = java.nio.file.Paths.get(hostPath).toAbsolutePath().toString(); - logger.info("Converting relative path to absolute: {}", hostPath); - } - java.io.File hostFile = new java.io.File(hostPath); - if (!hostFile.exists()) { - logger.warn("NonCopy mount host path does not exist: {}, skipping", hostPath); - continue; - } - volumeBindings.add(new VolumeBinding(hostPath, containerPath, "rw")); - logger.info("Added non Copy mount: {} -> {}", hostPath, containerPath); - } - } - - - Map runtimeConfig = Map.of(); - if (sandboxConfig != null) { - runtimeConfig = sandboxConfig.getRuntimeConfig(); - } - logger.info("Runtime config: {}", runtimeConfig); - String containerName; - String prefix = managerConfig.getContainerPrefixKey(); - if (prefix == null || prefix.isEmpty()) { - prefix = "sandbox"; - } - if (containerManagerType == ContainerManagerType.DOCKER) { - containerName = prefix + sessionId.toLowerCase(); - } else { - containerName = prefix.replace('_', '-') + sessionId.toLowerCase(); - } - // TODO: Need to check container name uniqueness? - ContainerCreateResult createResult; - createResult = containerClient.createContainer(containerName, imageName, ports, volumeBindings, environment, runtimeConfig); - - String containerId = createResult.getContainerId(); - if (containerId == null) { - logger.error("Container creation failed: containerId is null"); - return null; - } - List resultPorts = createResult.getPorts(); - String ip = createResult.getIp(); - String httpProtocol = createResult.getProtocol(); - - String firstPort = resultPorts != null && !resultPorts.isEmpty() ? resultPorts.get(0) : "80"; - - String baseHost = ip != null ? ip : "localhost"; - String accessPort = firstPort; - - String[] mappedPorts = resultPorts != null ? resultPorts.toArray(new String[0]) : new String[]{firstPort}; - - ContainerModel containerModel = ContainerModel.builder() - .sessionId(sessionId) - .containerId(containerId) - .containerName(containerName) - .baseUrl(String.format("%s://%s:%s/fastapi", httpProtocol, baseHost, accessPort)) - .browserUrl(String.format("%s://%s:%s/steel-api/%s", httpProtocol, baseHost, accessPort, runtimeToken)) - .frontBrowserWS(String.format("ws://%s:%s/steel-api/%s/v1/sessions/cast", baseHost, accessPort, runtimeToken)) - .clientBrowserWS(String.format("ws://%s:%s/steel-api/%s/&sessionId=%s", baseHost, accessPort, runtimeToken, BROWSER_SESSION_ID)) - .artifactsSIO(String.format("%s://%s:%s/v1", httpProtocol, baseHost, accessPort)) - .ports(mappedPorts) - .mountDir(mountDir) - .storagePath(storagePath) - .runtimeToken(runtimeToken) - .authToken(runtimeToken) - .version(imageName) - .build(); - - logger.info("Container Model: {}", containerModel); - - containerClient.startContainer(containerId); - return containerModel; - } - - private void releaseContainer(ContainerModel containerModel) { - if (containerModel == null) { - return; - } - try { - logger.info("Releasing container: {}", containerModel.getContainerName()); - portManager.releaseContainerPorts(containerModel.getContainerName()); - containerClient.stopContainer(containerModel.getContainerId()); - containerClient.removeContainer(containerModel.getContainerId()); - if (containerModel.getMountDir() != null && containerModel.getStoragePath() != null) { - storageManager.uploadFolder(containerModel.getMountDir(), containerModel.getStoragePath()); - } - } catch (Exception e) { - logger.warn("Error releasing container {}: {}", containerModel.getContainerName(), e.getMessage()); - } - } - - public ContainerModel getSandbox(SandboxType sandboxType, String userID, String sessionID) { - return getSandbox(sandboxType, null, null, null, userID, sessionID, null, null); - } - - public ContainerModel getSandbox(SandboxType sandboxType, String userID, String sessionID, String imageId, Map labels) { - return getSandbox(sandboxType, null, null, null, userID, sessionID, imageId, labels); - } - - public ContainerModel getSandbox(SandboxType sandboxType, String mountDir, String storagePath, Map environment, String userID, String sessionID) { - return getSandbox(sandboxType, mountDir, storagePath, environment, userID, sessionID, null, null); - } - - public ContainerModel getSandbox(SandboxType sandboxType, String mountDir, String storagePath, Map environment, String userID, String sessionID, String imageId, Map labels) { - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType); - - if (redisEnabled && redisContainerMapping != null) { - ContainerModel existingModel = redisContainerMapping.get(key); - if (existingModel != null) { - logger.info("Found existing container in Redis: {}", existingModel.getContainerName()); - // Also update local cache - sandboxMap.put(key, existingModel); - return existingModel; - } - } - - if (sandboxMap.containsKey(key)) { - return sandboxMap.get(key); - } else { - ContainerModel containerModel = createContainer(sandboxType, mountDir, storagePath, environment, imageId, labels); - sandboxMap.put(key, containerModel); - - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.put(key, containerModel); - logger.info("Stored container in Redis: {}", containerModel.getContainerName()); - } - - startSandbox(sandboxType, userID, sessionID); - return containerModel; - } - } - - @RemoteWrapper - public void startSandbox(SandboxType sandboxType, String userID, String sessionID) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding startSandbox to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxType", sandboxType != null ? sandboxType.name() : null); - requestData.put("userID", userID); - requestData.put("sessionID", sessionID); - remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/startSandbox", - requestData, - "data" - ); - return; - } - - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType); - ContainerModel containerModel = sandboxMap.get(key); - - if (containerModel == null && redisEnabled && redisContainerMapping != null) { - containerModel = redisContainerMapping.get(key); - } - - if (containerModel != null) { - containerClient.startContainer(containerModel.getContainerId()); - logger.info("Container status updated to: running"); - sandboxMap.put(key, containerModel); - - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.put(key, containerModel); - } - } - } - - @RemoteWrapper - public void stopSandbox(SandboxType sandboxType, String userID, String sessionID) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding stopSandbox to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxType", sandboxType != null ? sandboxType.name() : null); - requestData.put("userID", userID); - requestData.put("sessionID", sessionID); - remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/stopSandbox", - requestData, - "data" - ); - return; - } - - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType); - ContainerModel containerModel = sandboxMap.get(key); - - if (containerModel == null && redisEnabled && redisContainerMapping != null) { - containerModel = redisContainerMapping.get(key); - } - - if (containerModel != null) { - containerClient.stopContainer(containerModel.getContainerId()); - logger.info("Container status updated to: stopped"); - sandboxMap.put(key, containerModel); - - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.put(key, containerModel); - } - } - } - - public void removeSandbox(SandboxType sandboxType, String userID, String sessionID) { - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType); - ContainerModel containerModel = sandboxMap.get(key); - - if (containerModel == null && redisEnabled && redisContainerMapping != null) { - containerModel = redisContainerMapping.get(key); - } - - if (containerModel != null) { - containerClient.removeContainer(containerModel.getContainerId()); - sandboxMap.remove(key); - - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.remove(key); - logger.info("Removed container from Redis"); - } - } - } - - public void stopAndRemoveSandbox(SandboxType sandboxType, String userID, String sessionID) { - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType); - ContainerModel containerModel = sandboxMap.get(key); - - if (containerModel == null && redisEnabled && redisContainerMapping != null) { - containerModel = redisContainerMapping.get(key); - } - - if (containerModel != null) { - try { - String containerId = containerModel.getContainerId(); - String containerName = containerModel.getContainerName(); - logger.info("Stopping and removing {} sandbox (Container ID: {}, Name: {})", sandboxType, containerId, containerName); - - if (sandboxType == SandboxType.AGENTBAY) { - agentBayClient.removeContainer(containerId); - logger.info("Deleted AgentBay container: {}", containerName); - } else { - portManager.releaseContainerPorts(containerName); - containerClient.stopContainer(containerId); - Thread.sleep(1000); - containerClient.removeContainer(containerId); - } - sandboxMap.remove(key); - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.remove(key); - logger.info("Removed container from Redis"); - } - logger.info("{} sandbox has been successfully removed", sandboxType); - } catch (Exception e) { - logger.error("Error removing {} sandbox: {}", sandboxType, e.getMessage()); - sandboxMap.remove(key); - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.remove(key); - } - } - } else { - logger.warn("Sandbox {} not found, may have already been removed", sandboxType); - } - } - - @RemoteWrapper - public String getSandboxStatus(SandboxType sandboxType, String userID, String sessionID) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding getSandboxStatus to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxType", sandboxType != null ? sandboxType.name() : null); - requestData.put("userID", userID); - requestData.put("sessionID", sessionID); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/getSandboxStatus", - requestData, - "data" - ); - return result != null ? result.toString() : null; - } - - SandboxKey key = new SandboxKey(userID, sessionID, sandboxType); - ContainerModel containerModel = sandboxMap.get(key); - - if (containerModel == null && redisEnabled && redisContainerMapping != null) { - containerModel = redisContainerMapping.get(key); - if (containerModel != null) { - sandboxMap.put(key, containerModel); - } - } - - if (containerModel != null) { - return containerClient.getContainerStatus(containerModel.getContainerId()); - } - return "not_found"; - } - - public Map getAllSandboxes() { - Map allSandboxes = new HashMap<>(sandboxMap); - - if (redisEnabled && redisContainerMapping != null) { - try { - Map redisData = redisContainerMapping.getAll(); - logger.info("Retrieved {} containers from Redis", redisData.size()); - - allSandboxes.putAll(redisData); - } catch (Exception e) { - logger.warn("Failed to retrieve containers from Redis: {}", e.getMessage()); - } - } - - return allSandboxes; - } - - @RemoteWrapper - public ContainerModel getInfo(String identity) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding getInfo to remote server"); - Map requestData = new HashMap<>(); - requestData.put("identity", identity); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/getInfo", - requestData, - "data" - ); - if (result instanceof Map) { - @SuppressWarnings("unchecked") - Map resultMap = (Map) result; - return ContainerModel.fromMap(resultMap); - } - return null; - } - - if (identity == null || identity.isEmpty()) { - throw new IllegalArgumentException("Identity cannot be null or empty"); - } - - for (ContainerModel model : sandboxMap.values()) { - if (identity.equals(model.getContainerName())) { - logger.info("Found container by name: {}", identity); - return model; - } - } - - for (ContainerModel model : sandboxMap.values()) { - if (identity.equals(model.getSessionId())) { - logger.info("Found container by session ID: {}", identity); - return model; - } - } - - for (ContainerModel model : sandboxMap.values()) { - if (identity.equals(model.getContainerId())) { - logger.info("Found container by container ID: {}", identity); - return model; - } - } - - for (ContainerModel model : sandboxMap.values()) { - if (identity.equals(model.getContainerName())) { - logger.info("Found container by prefixed session ID: {}", identity); - return model; - } - } - - throw new RuntimeException("No container found with identity: " + identity); - } - - - @RemoteWrapper - public boolean release(String identity) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding release to remote server"); - Map requestData = new HashMap<>(); - requestData.put("identity", identity); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/release", - requestData, - "data" - ); - return result instanceof Boolean ? (Boolean) result : false; - } - - try { - ContainerModel containerModel = getInfo(identity); - logger.info("Releasing container with identity: {}", identity); - SandboxKey keyToRemove = null; - - for (Map.Entry entry : sandboxMap.entrySet()) { - if (entry.getValue().getContainerName().equals(containerModel.getContainerName())) { - keyToRemove = entry.getKey(); - break; - } - } - - if (keyToRemove != null) { - sandboxMap.remove(keyToRemove); - logger.info("Removed container from sandbox map: {}", keyToRemove); - - if (redisEnabled && redisContainerMapping != null) { - redisContainerMapping.remove(keyToRemove); - logger.info("Removed container from Redis"); - } - } - - portManager.releaseContainerPorts(containerModel.getContainerName()); - - if (keyToRemove != null && keyToRemove.getSandboxType() == SandboxType.AGENTBAY) { - agentBayClient.removeContainer(containerModel.getContainerId()); - logger.info("Deleted AgentBay container: {}", containerModel.getContainerName()); - return true; - } - - containerClient.stopContainer(containerModel.getContainerId()); - containerClient.removeContainer(containerModel.getContainerId()); - logger.info("Container destroyed: {}", containerModel.getContainerName()); - if (containerModel.getMountDir() != null && containerModel.getStoragePath() != null) { - try { - logger.info("Uploading container data to storage: {}", containerModel.getStoragePath()); - boolean uploaded = storageManager.uploadFolder(containerModel.getMountDir(), containerModel.getStoragePath()); - if (uploaded) { - logger.info("Successfully uploaded container data to storage"); - } else { - logger.warn("Failed to upload container data to storage"); - } - } catch (Exception e) { - logger.warn("Failed to upload to storage: {}", e.getMessage()); - } - } - - return true; - - } catch (RuntimeException e) { - logger.warn("Container not found for identity: {}", identity); - return false; - } catch (Exception e) { - logger.error("Failed to release container: {}", e.getMessage()); - e.printStackTrace(); - return false; - } - } - - public ContainerManagerType getContainerManagerType() { - return containerManagerType; - } - - public ManagerConfig getManagerConfig() { - return managerConfig; - } - - public BaseClient getContainerClient() { - return containerClient; - } - - public StorageManager getStorageManager() { - return storageManager; - } - - public RemoteHttpClient getRemoteHttpClient() { - return remoteHttpClient; - } - - @RemoteWrapper - public void cleanupAllSandboxes() { - logger.info("Starting cleanup of all sandbox containers..."); - - if (poolQueue != null) { - try { - logger.info("Cleaning up container pool (current size: {})", poolQueue.size()); - while (!poolQueue.isEmpty()) { - ContainerModel containerModel = poolQueue.dequeue(); - if (containerModel != null) { - logger.info("Destroying pool container: {}", containerModel.getContainerName()); - release(containerModel.getContainerId()); - } - } - logger.info("Container pool cleanup complete"); - } catch (Exception e) { - logger.error("Error cleaning up container pool: {}", e.getMessage()); - } - } else { - logger.info("Remote mode: no local pool to cleanup"); - } - - - if (sandboxMap.isEmpty()) { - logger.info("No additional sandbox containers to clean up"); - } else { - logger.info("Cleaning up {} active sandbox containers", sandboxMap.size()); - logger.info("Sandbox types: {}", sandboxMap.keySet()); - - for (SandboxKey key : new HashSet<>(sandboxMap.keySet())) { - try { - logger.info("Cleaning up {} sandbox for user {} session {}", key.getSandboxType(), key.getUserID(), key.getSessionID()); - stopAndRemoveSandbox(key.getSandboxType(), key.getUserID(), key.getSessionID()); - logger.info("{} sandbox cleanup complete", key.getSandboxType()); - } catch (Exception e) { - logger.error("Error cleaning up {} sandbox: {}", key.getSandboxType(), e.getMessage()); - } - } - - sandboxMap.clear(); - } - - logger.info("All sandbox containers have been cleaned up!"); - } - - @Override - public void close() { - logger.info("Closing SandboxManager and cleaning up all resources"); - - cleanupAllSandboxes(); - - if (storageManager != null) { - storageManager.close(); - } - - if (portManager != null) { - portManager.clear(); - } - - if (redisEnabled && redisClient != null) { - try { - redisClient.close(); - logger.info("Redis connection closed"); - } catch (Exception e) { - logger.warn("Error closing Redis connection: {}", e.getMessage()); - } - } - - logger.info("SandboxManager closed successfully"); - } - - public String listTools(String identity, String toolType) { - return ""; - } - - private SandboxClient establishConnection(String sandboxId) { - try { - ContainerModel containerInfo = getInfo(sandboxId); - if (containerInfo.getVersion().contains("sandbox-appworld") || containerInfo.getVersion().contains("sandbox-bfcl")) { - return new TrainingSandboxClient(containerInfo, 60); - } - return new SandboxHttpClient(containerInfo, 60); - } catch (Exception e) { - logger.error("Failed to establish connection to sandbox: {}", e.getMessage()); - throw new RuntimeException("Failed to establish connection", e); - } - } - - @RemoteWrapper - public Map listTools(String sandboxId, String userId, String sessionId, String toolType) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding listTools to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxId", sandboxId); - requestData.put("userId", userId); - requestData.put("sessionId", sessionId); - requestData.put("toolType", toolType); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/listTools", - requestData, - "data" - ); - if (result instanceof Map) { - @SuppressWarnings("unchecked") - Map resultMap = (Map) result; - return resultMap; - } - return new HashMap<>(); - } - - try (SandboxClient client = establishConnection(sandboxId)) { - return client.listTools(toolType, Map.of()); - } catch (Exception e) { - logger.error("Error listing tools: {}", e.getMessage()); - return new HashMap<>(); - } - } - - @RemoteWrapper - public String callTool(String sandboxId, String userId, String sessionId, String toolName, Map arguments) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding callTool to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxId", sandboxId); - requestData.put("userId", userId); - requestData.put("sessionId", sessionId); - requestData.put("toolName", toolName); - requestData.put("arguments", arguments); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/callTool", - requestData, - "data" - ); - return result != null ? result.toString() : null; - } - - try (SandboxClient client = establishConnection(sandboxId)) { - return client.callTool(toolName, arguments); - } catch (Exception e) { - logger.error("Error calling tool {}: {}", toolName, e.getMessage()); - e.printStackTrace(); - return "{\"isError\":true,\"content\":[{\"type\":\"text\",\"text\":\"Error calling tool: " + e.getMessage() + "\"}]}"; - } - } - - @RemoteWrapper - public Map addMcpServers(String sandboxId, String userId, String sessionId, Map serverConfigs, boolean overwrite) { - if (remoteHttpClient != null && remoteHttpClient.isConfigured()) { - logger.info("Remote mode: forwarding addMcpServers to remote server"); - Map requestData = new HashMap<>(); - requestData.put("sandboxId", sandboxId); - requestData.put("userId", userId); - requestData.put("sessionId", sessionId); - requestData.put("serverConfigs", serverConfigs); - requestData.put("overwrite", overwrite); - Object result = remoteHttpClient.makeRequest( - RequestMethod.POST, - "/sandbox/addMcpServers", - requestData, - "data" - ); - if (result instanceof Map) { - @SuppressWarnings("unchecked") - Map resultMap = (Map) result; - return resultMap; - } - return new HashMap<>(); - } - - try (SandboxClient client = establishConnection(sandboxId)) { - return client.addMcpServers(serverConfigs, overwrite); - } catch (Exception e) { - logger.error("Error adding MCP servers: {}", e.getMessage()); - return new HashMap<>(); - } - } - - public boolean releaseSandbox(SandboxType sandboxType, String userId, String sessionId) { - try { - stopAndRemoveSandbox(sandboxType, userId, sessionId); - logger.info("Released sandbox: type={}, user={}, session={}", sandboxType, userId, sessionId); - return true; - } catch (Exception e) { - logger.error("Failed to release sandbox: {}", e.getMessage()); - return false; - } - } -} \ No newline at end of file diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/ContainerQueue.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/ContainerQueue.java deleted file mode 100644 index 13f3607f..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/ContainerQueue.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.collections; - -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; - -/** - * Queue interface for container pool management - */ -public interface ContainerQueue { - - /** - * Add a container to the queue - * - * @param item The container model to enqueue - */ - void enqueue(ContainerModel item); - - /** - * Remove and return a container from the queue - * - * @return The container model, or null if queue is empty - */ - ContainerModel dequeue(); - - /** - * Return the first container without removing it - * - * @return The container model, or null if queue is empty - */ - ContainerModel peek(); - - /** - * Check if the queue is empty - * - * @return true if empty, false otherwise - */ - boolean isEmpty(); - - /** - * Get the number of containers in the queue - * - * @return The size of the queue - */ - int size(); - - /** - * Clear all containers from the queue - */ - void clear(); -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/InMemoryContainerQueue.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/InMemoryContainerQueue.java deleted file mode 100644 index 5d8106d7..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/InMemoryContainerQueue.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.collections; - -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * In-memory implementation of ContainerQueue - */ -public class InMemoryContainerQueue implements ContainerQueue { - - private final Queue queue; - - public InMemoryContainerQueue() { - this.queue = new LinkedList<>(); - } - - @Override - public synchronized void enqueue(ContainerModel item) { - if (item != null) { - queue.offer(item); - } - } - - @Override - public synchronized ContainerModel dequeue() { - return queue.poll(); - } - - @Override - public synchronized ContainerModel peek() { - return queue.peek(); - } - - @Override - public synchronized boolean isEmpty() { - return queue.isEmpty(); - } - - @Override - public synchronized int size() { - return queue.size(); - } - - @Override - public synchronized void clear() { - queue.clear(); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/RedisContainerMapping.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/RedisContainerMapping.java deleted file mode 100644 index 3cd7384f..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/RedisContainerMapping.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.collections; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxKey; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.util.RedisClientWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * Redis-backed implementation for container mapping - * Maps SandboxKey to ContainerModel using Redis - */ -public class RedisContainerMapping { - - private static final Logger logger = LoggerFactory.getLogger(RedisContainerMapping.class); - - private final RedisClientWrapper redisClient; - private final String prefix; - private final ObjectMapper objectMapper; - - public RedisContainerMapping(RedisClientWrapper redisClient, String prefix) { - this.redisClient = redisClient; - this.prefix = (prefix != null && !prefix.isEmpty()) ? - (prefix.endsWith(":") ? prefix : prefix + ":") : ""; - this.objectMapper = new ObjectMapper(); - logger.info("Redis container mapping initialized with prefix: {}", this.prefix); - } - - private String getFullKey(SandboxKey key) { - return String.format("%s%s:%s:%s:%s", - prefix, - key.getUserID(), - key.getSessionID(), - key.getSandboxType().getTypeName(), - key.getImageID()); - } - - private String getFullKey(String key) { - return prefix + key; - } - - private String stripPrefix(String fullKey) { - if (prefix != null && !prefix.isEmpty() && fullKey.startsWith(prefix)) { - return fullKey.substring(prefix.length()); - } - return fullKey; - } - - public void put(SandboxKey key, ContainerModel value) { - if (key == null || value == null) { - logger.warn("Attempted to put null key or value, skipping"); - return; - } - - try { - String fullKey = getFullKey(key); - String json = objectMapper.writeValueAsString(value); - redisClient.set(fullKey, json); - logger.info("Put container in Redis: {} with key: {}", value.getContainerName(), fullKey); - } catch (JsonProcessingException e) { - logger.error("Failed to serialize container model: {}", e.getMessage()); - throw new RuntimeException("Failed to put container in Redis", e); - } - } - - public ContainerModel get(SandboxKey key) { - if (key == null) { - return null; - } - - try { - String fullKey = getFullKey(key); - String json = redisClient.get(fullKey); - - if (json == null || json.isEmpty()) { - return null; - } - - ContainerModel model = objectMapper.readValue(json, ContainerModel.class); - logger.info("Retrieved container from Redis: {}", model.getContainerName()); - return model; - } catch (JsonProcessingException e) { - logger.error("Failed to deserialize container model: {}", e.getMessage()); - throw new RuntimeException("Failed to get container from Redis", e); - } - } - - public boolean remove(SandboxKey key) { - if (key == null) { - return false; - } - - String fullKey = getFullKey(key); - Long deleted = redisClient.delete(fullKey); - boolean removed = deleted != null && deleted > 0; - - if (removed) { - logger.info("Removed container from Redis with key: {}", fullKey); - } - - return removed; - } - - public boolean containsKey(SandboxKey key) { - if (key == null) { - return false; - } - - String fullKey = getFullKey(key); - return redisClient.exists(fullKey); - } - - public Set scan(String pattern) { - String searchPattern = getFullKey(pattern) + "*"; - Set fullKeys = redisClient.scan(searchPattern); - - Set keys = new java.util.HashSet<>(); - for (String fullKey : fullKeys) { - keys.add(stripPrefix(fullKey)); - } - - return keys; - } - - public Map getAll() { - Map result = new HashMap<>(); - - String searchPattern = prefix + "*"; - Set fullKeys = redisClient.scan(searchPattern); - - for (String fullKey : fullKeys) { - try { - String json = redisClient.get(fullKey); - if (json != null && !json.isEmpty()) { - ContainerModel model = objectMapper.readValue(json, ContainerModel.class); - - String keyWithoutPrefix = stripPrefix(fullKey); - SandboxKey sandboxKey = parseSandboxKey(keyWithoutPrefix); - - if (sandboxKey != null) { - result.put(sandboxKey, model); - } - } - } catch (Exception e) { - logger.warn("Failed to deserialize container for key {}: {}", fullKey, e.getMessage()); - } - } - - return result; - } - - private SandboxKey parseSandboxKey(String keyString) { - if (keyString == null || keyString.isEmpty()) { - return null; - } - - try { - String[] parts = keyString.split(":", 3); - if (parts.length != 3) { - logger.warn("Invalid key format: {}", keyString); - return null; - } - - String userID = parts[0]; - String sessionID = parts[1]; - String sandboxTypeStr = parts[2]; - - SandboxType sandboxType = SandboxType.valueOf(sandboxTypeStr.toUpperCase()); - - return new SandboxKey(userID, sessionID, sandboxType); - } catch (Exception e) { - logger.warn("Failed to parse SandboxKey from: {}, error: {}", keyString, e.getMessage()); - return null; - } - } - - public void clear() { - String searchPattern = prefix + "*"; - Set fullKeys = redisClient.scan(searchPattern); - - for (String fullKey : fullKeys) { - redisClient.delete(fullKey); - } - - logger.info("Cleared all entries with prefix: {}", prefix); - } - - public int size() { - String searchPattern = prefix + "*"; - Set fullKeys = redisClient.scan(searchPattern); - return fullKeys.size(); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/RedisContainerQueue.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/RedisContainerQueue.java deleted file mode 100644 index 0674689e..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/collections/RedisContainerQueue.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.collections; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.util.RedisClientWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Redis-backed implementation of ContainerQueue - */ -public class RedisContainerQueue implements ContainerQueue { - - private static final Logger logger = LoggerFactory.getLogger(RedisContainerQueue.class); - - private final RedisClientWrapper redisClient; - private final String queueName; - private final ObjectMapper objectMapper; - - public RedisContainerQueue(RedisClientWrapper redisClient, String queueName) { - this.redisClient = redisClient; - this.queueName = queueName; - this.objectMapper = new ObjectMapper(); - logger.info("Redis container queue initialized with name: {}", queueName); - } - - @Override - public void enqueue(ContainerModel item) { - if (item == null) { - logger.warn("Attempted to enqueue null item, skipping"); - return; - } - - try { - String json = objectMapper.writeValueAsString(item); - redisClient.rpush(queueName, json); - logger.info("Enqueued container: {}", item.getContainerName()); - } catch (JsonProcessingException e) { - logger.error("Failed to serialize container model: {}", e.getMessage()); - throw new RuntimeException("Failed to enqueue container", e); - } - } - - @Override - public ContainerModel dequeue() { - try { - String json = redisClient.lpop(queueName); - if (json == null || json.isEmpty()) { - return null; - } - - ContainerModel model = objectMapper.readValue(json, ContainerModel.class); - logger.info("Dequeued container: {}", model.getContainerName()); - return model; - } catch (JsonProcessingException e) { - logger.error("Failed to deserialize container model: {}", e.getMessage()); - throw new RuntimeException("Failed to dequeue container", e); - } - } - - @Override - public ContainerModel peek() { - try { - String json = redisClient.lindex(queueName, 0); - if (json == null || json.isEmpty()) { - return null; - } - - return objectMapper.readValue(json, ContainerModel.class); - } catch (JsonProcessingException e) { - logger.error("Failed to deserialize container model: {}", e.getMessage()); - throw new RuntimeException("Failed to peek container", e); - } - } - - @Override - public boolean isEmpty() { - Long length = redisClient.llen(queueName); - return length == null || length == 0; - } - - @Override - public int size() { - Long length = redisClient.llen(queueName); - return length != null ? length.intValue() : 0; - } - - @Override - public void clear() { - redisClient.ltrim(queueName); - logger.info("Cleared Redis queue: {}", queueName); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/ManagerConfig.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/ManagerConfig.java deleted file mode 100644 index a7732968..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/ManagerConfig.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.model; - -import io.agentscope.runtime.sandbox.manager.client.config.AgentRunClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerManagerType; -import io.agentscope.runtime.sandbox.manager.model.container.PortRange; -import io.agentscope.runtime.sandbox.manager.model.container.RedisManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemConfig; -import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemType; -import io.agentscope.runtime.sandbox.manager.model.fs.LocalFileSystemConfig; -import io.agentscope.runtime.sandbox.manager.model.fs.OssConfig; - -import java.io.File; -import java.util.List; - -/** - * Sandbox Manager Environment Configuration Class - */ -public class ManagerConfig { - - private static final int UUID_LENGTH = 25; - - private String containerPrefixKey; - - private Boolean redisEnabled = false; - - private String baseUrl; - private String bearerToken; - - - private PortRange portRange; - private int poolSize = 0; - private List defaultSandboxType; - - private BaseClientConfig clientConfig; - private FileSystemConfig fileSystemConfig; - private RedisManagerConfig redisConfig; - - public String getAgentBayApiKey() { - return agentBayApiKey; - } - - - private String agentBayApiKey; - - private ManagerConfig(Builder builder) { - this.containerPrefixKey = builder.containerPrefixKey; - this.redisEnabled = builder.redisEnabled; - this.redisConfig = builder.redisConfig; - - this.clientConfig = builder.clientConfig; - this.fileSystemConfig = builder.fileSystemConfig; - this.portRange = builder.portRange; - this.poolSize = builder.poolSize; - - this.baseUrl = builder.baseUrl; - this.bearerToken = builder.bearerToken; - this.defaultSandboxType = builder.defaultSandboxType; - - this.agentBayApiKey = builder.agentBayApiKey == null ? System.getenv("AGENTBAY_API_KEY") : builder.agentBayApiKey; - this.agentBayApiKey = this.agentBayApiKey == null ? this.bearerToken : this.agentBayApiKey; - - validate(); - } - - private void validate() { - if (fileSystemConfig.getFileSystemType() == FileSystemType.LOCAL && fileSystemConfig instanceof LocalFileSystemConfig localFileSystemConfig) { - File dir = new File(localFileSystemConfig.getMountDir()); - if (!dir.exists()) { - dir.mkdirs(); - } - } - - if (fileSystemConfig.getFileSystemType() == FileSystemType.OSS && fileSystemConfig instanceof OssConfig ossConfig) { - validateRequired("ossEndpoint", ossConfig.getOssEndpoint(), "file_system is 'oss'"); - validateRequired("ossAccessKeyId", ossConfig.getOssAccessKeyId(), "file_system is 'oss'"); - validateRequired("ossAccessKeySecret", ossConfig.getOssAccessKeySecret(), "file_system is 'oss'"); - validateRequired("ossBucketName", ossConfig.getOssBucketName(), "file_system is 'oss'"); - } - - if (redisEnabled) { - validateRequired("redisServer", redisConfig.getRedisServer(), "redis is enabled"); - validateRequired("redisPort", redisConfig.getRedisPort(), "redis is enabled"); - validateRequired("redisDb", redisConfig.getRedisDb(), "redis is enabled"); - validateRequired("redisPortKey", redisConfig.getRedisPortKey(), "redis is enabled"); - validateRequired("redisContainerPoolKey", redisConfig.getRedisContainerPoolKey(), "redis is enabled"); - } - - if (clientConfig.getClientType() == ContainerManagerType.AGENTRUN && clientConfig instanceof AgentRunClientConfig agentRunClientConfig) { - validateRequired("agentRunAccessKeyId", agentRunClientConfig.getAgentRunAccessKeyId(), "container_deployment is 'agentrun'"); - validateRequired("agentRunAccessKeySecret", agentRunClientConfig.getAgentRunAccessKeySecret(), "container_deployment is 'agentrun'"); - validateRequired("agentRunAccountId", agentRunClientConfig.getAgentRunAccountId(), "container_deployment is 'agentrun'"); - } - } - - private void validateRequired(String fieldName, Object value, String condition) { - if (value == null || (value instanceof String && ((String) value).isEmpty())) { - throw new IllegalArgumentException(fieldName + " must be set when " + condition); - } - } - - public List getDefaultSandboxType() { - return defaultSandboxType; - } - - public String getContainerPrefixKey() { - return containerPrefixKey; - } - - public Boolean getRedisEnabled() { - return redisEnabled; - } - - public BaseClientConfig getClientConfig() { - return clientConfig; - } - - public PortRange getPortRange() { - return portRange; - } - - public int getPoolSize() { - return poolSize; - } - - public FileSystemConfig getFileSystemConfig() { - return fileSystemConfig; - } - - public RedisManagerConfig getRedisConfig() { - return redisConfig; - } - - public String getBaseUrl() { - return this.baseUrl; - } - - public String getBearerToken() { - return this.bearerToken; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private String containerPrefixKey = "runtime_sandbox_container_"; - private Boolean redisEnabled = false; - private BaseClientConfig clientConfig = DockerClientConfig.builder().build(); - private RedisManagerConfig redisConfig; - private PortRange portRange = new PortRange(49152, 59152); - private int poolSize = 0; - private String baseUrl; - private String bearerToken; - private List defaultSandboxType = List.of(SandboxType.BASE); - private String agentBayApiKey; - - private FileSystemConfig fileSystemConfig = LocalFileSystemConfig.builder().build(); - - public Builder containerPrefixKey(String containerPrefixKey) { - if (containerPrefixKey.length() > 63 - UUID_LENGTH) { - throw new IllegalArgumentException("containerPrefixKey max length is " + (63 - UUID_LENGTH)); - } - this.containerPrefixKey = containerPrefixKey; - return this; - } - - public Builder storageFolder(String storageFolder) { - return this; - } - - public Builder redisConfig(RedisManagerConfig redisConfig) { - this.redisConfig = redisConfig; - this.redisEnabled = true; - return this; - } - - public Builder bearerToken(String bearerToken) { - this.bearerToken = bearerToken; - return this; - } - - public Builder baseUrl(String baseUrl) { - this.baseUrl = baseUrl; - return this; - } - - public Builder containerDeployment(BaseClientConfig clientConfig) { - this.clientConfig = clientConfig; - return this; - } - - public Builder portRange(PortRange portRange) { - this.portRange = portRange; - return this; - } - - public Builder poolSize(int poolSize) { - this.poolSize = poolSize; - return this; - } - - public Builder fileSystemConfig(FileSystemConfig fileSystemConfig) { - this.fileSystemConfig = fileSystemConfig; - return this; - } - - public Builder defaultSandboxType(List defaultSandboxType) { - this.defaultSandboxType = defaultSandboxType; - return this; - } - - public Builder agentBayApiKey(String agentBayApiKey) { - this.agentBayApiKey = agentBayApiKey; - return this; - } - - public ManagerConfig build() { - return new ManagerConfig(this); - } - } -} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/DockerProp.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/DockerProp.java deleted file mode 100644 index 1b8af388..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/DockerProp.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.model.container; - -import java.util.HashMap; -import java.util.Map; - -/** - * Docker container properties configuration class - */ -public class DockerProp { - private String imageName; - private String containerName; - private Map portMap; - private Map volumeBindings; - private Map environment; - private String runtimeConfig; - - public DockerProp() { - this.portMap = new HashMap<>(); - this.volumeBindings = new HashMap<>(); - this.environment = new HashMap<>(); - } - - public DockerProp(String imageName, String containerName) { - this(); - this.imageName = imageName; - this.containerName = containerName; - } - - public String getImageName() { - return imageName; - } - - public void setImageName(String imageName) { - this.imageName = imageName; - } - - public String getContainerName() { - return containerName; - } - - public void setContainerName(String containerName) { - this.containerName = containerName; - } - - public Map getPartMap() { - return portMap; - } - - public void setPortMap(Map portMap) { - this.portMap = portMap; - } - - public Map getVolumeBindings() { - return volumeBindings; - } - - public void setVolumeBindings(Map volumeBindings) { - this.volumeBindings = volumeBindings; - } - - public Map getEnvironment() { - return environment; - } - - public void setEnvironment(Map environment) { - this.environment = environment; - } - - public String getRuntimeConfig() { - return runtimeConfig; - } - - public void setRuntimeConfig(String runtimeConfig) { - this.runtimeConfig = runtimeConfig; - } - - public DockerProp addPortMapping(Integer containerPort, Integer hostPort) { - this.portMap.put(containerPort, hostPort); - return this; - } - - public DockerProp addVolumeBinding(String hostPath, String containerPath) { - this.volumeBindings.put(hostPath, containerPath); - return this; - } - - public DockerProp addEnvironment(String key, String value) { - this.environment.put(key, value); - return this; - } -} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/DynamicSandboxType.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/DynamicSandboxType.java deleted file mode 100644 index 649255ca..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/DynamicSandboxType.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.model.container; - -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Dynamic sandbox type that allows runtime registration of custom types - */ -public class DynamicSandboxType { - private static final Map CUSTOM_TYPES = new ConcurrentHashMap<>(); - - public static final DynamicSandboxType BASE = fromEnum(SandboxType.BASE); - public static final DynamicSandboxType BROWSER = fromEnum(SandboxType.BROWSER); - public static final DynamicSandboxType FILESYSTEM = fromEnum(SandboxType.FILESYSTEM); - public static final DynamicSandboxType TRAINING = fromEnum(SandboxType.TRAINING); - public static final DynamicSandboxType APPWORLD = fromEnum(SandboxType.APPWORLD); - public static final DynamicSandboxType BFCL = fromEnum(SandboxType.BFCL); - public static final DynamicSandboxType WEBSHOP = fromEnum(SandboxType.WEBSHOP); - public static final DynamicSandboxType GUI = fromEnum(SandboxType.GUI); - public static final DynamicSandboxType MOBILE = fromEnum(SandboxType.MOBILE); - public static final DynamicSandboxType AGENTBAY = fromEnum(SandboxType.AGENTBAY); - - private final String typeName; - private final SandboxType enumType; - - private DynamicSandboxType(String typeName, SandboxType enumType) { - this.typeName = typeName; - this.enumType = enumType; - } - - public static DynamicSandboxType fromEnum(SandboxType sandboxType) { - return new DynamicSandboxType(sandboxType.getTypeName(), sandboxType); - } - - public static DynamicSandboxType custom(String typeName) { - if (typeName == null || typeName.trim().isEmpty()) { - throw new IllegalArgumentException("Type name cannot be null or empty"); - } - - String normalizedName = typeName.toLowerCase(); - - try { - SandboxType enumType = SandboxType.valueOf(typeName.toUpperCase()); - return fromEnum(enumType); - } catch (IllegalArgumentException e) { - // Not a predefined type, create or get custom type - } - - return CUSTOM_TYPES.computeIfAbsent(normalizedName, name -> new DynamicSandboxType(name, null)); - } - - public static DynamicSandboxType valueOf(String typeName) { - if (typeName == null || typeName.trim().isEmpty()) { - throw new IllegalArgumentException("Type name cannot be null or empty"); - } - - try { - SandboxType enumType = SandboxType.valueOf(typeName.toUpperCase()); - return fromEnum(enumType); - } catch (IllegalArgumentException e) { - String normalizedName = typeName.toLowerCase(); - DynamicSandboxType customType = CUSTOM_TYPES.get(normalizedName); - if (customType != null) { - return customType; - } - throw new IllegalArgumentException("Unknown sandbox type: " + typeName); - } - } - - public boolean isEnum() { - return enumType != null; - } - - public boolean isCustom() { - return enumType == null; - } - - public SandboxType getEnumType() { - return enumType; - } - - public String getTypeName() { - return typeName; - } - - public SandboxType toEnum() { - if (enumType == null) { - throw new UnsupportedOperationException("Cannot convert custom type '" + typeName + "' to SandboxType enum"); - } - return enumType; - } - - public static Map getCustomTypes() { - return new ConcurrentHashMap<>(CUSTOM_TYPES); - } - - public static boolean removeCustomType(String typeName) { - if (typeName == null) { - return false; - } - String normalizedName = typeName.toLowerCase(); - return CUSTOM_TYPES.remove(normalizedName) != null; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DynamicSandboxType that = (DynamicSandboxType) o; - return Objects.equals(typeName, that.typeName); - } - - @Override - public int hashCode() { - return Objects.hash(typeName); - } - - @Override - public String toString() { - return "DynamicSandboxType{" + "typeName='" + typeName + '\'' + ", isCustom=" + isCustom() + '}'; - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxKey.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxKey.java deleted file mode 100644 index cb5df1f3..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxKey.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.model.container; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Objects; - -public class SandboxKey { - private static final Logger logger = LoggerFactory.getLogger(SandboxKey.class); - private final String userID; - private final String sessionID; - private final SandboxType sandboxType; - private final String imageID; - - public SandboxKey(String userID, String sessionID, SandboxType sandboxType, String imageID) { - this.userID = userID; - this.sessionID = sessionID; - this.sandboxType = sandboxType; - this.imageID = imageID; - } - - public SandboxKey(String userID, String sessionID, SandboxType sandboxType) { - this(userID, sessionID, sandboxType, sandboxType == SandboxType.AGENTBAY ? "linux_latest" : ""); - if (sandboxType == SandboxType.AGENTBAY) { - logger.warn("Creating SandboxKey without default \"linux_latest\" imageID for AGENTBAY type."); - } - } - - public String getUserID() { - return userID; - } - - public String getSessionID() { - return sessionID; - } - - public SandboxType getSandboxType() { - return sandboxType; - } - - public String getImageID() { - return imageID; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SandboxKey that = (SandboxKey) o; - if(sandboxType != SandboxType.AGENTBAY){ - return Objects.equals(userID, that.userID) && Objects.equals(sessionID, that.sessionID) && sandboxType == that.sandboxType; - } - return Objects.equals(userID, that.userID) && Objects.equals(sessionID, that.sessionID) && sandboxType == that.sandboxType && Objects.equals(imageID, that.imageID); - } - - @Override - public int hashCode() { - return Objects.hash(userID, sessionID, sandboxType, imageID); - } - - @Override - public String toString() { - return "SandboxKey{" + "userID='" + userID + '\'' + ", sessionID='" + sessionID + '\'' + ", sandboxType=" + sandboxType + '\'' + ", imageId=" + imageID + '}'; - } -} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistry.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistry.java deleted file mode 100644 index 82bdbf50..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistry.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.registry; - -import io.agentscope.runtime.sandbox.manager.model.container.DynamicSandboxType; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxConfig; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Sandbox registry for managing sandbox configurations - */ -public class SandboxRegistry { - private static final Logger logger = LoggerFactory.getLogger(SandboxRegistry.class); - - private static final Map, SandboxConfig> classRegistry = new ConcurrentHashMap<>(); - private static final Map> typeRegistry = new ConcurrentHashMap<>(); - private static final Map typeConfigRegistry = new ConcurrentHashMap<>(); - - private static final Map customTypeRegistry = new ConcurrentHashMap<>(); - - static { - try { - Class.forName(SandboxRegistryInitializer.class.getName()); - logger.info("SandboxRegistryInitializer loaded and executed"); - } catch (ClassNotFoundException e) { - logger.warn("SandboxRegistryInitializer not found, annotation-based registration disabled"); - } - } - - /** - * Register a sandbox configuration for a specific class and type - * - * @param targetClass The class to register - * @param config The sandbox configuration - */ - public static void register(Class targetClass, SandboxConfig config) { - if (targetClass == null) { - throw new IllegalArgumentException("Target class cannot be null"); - } - if (config == null) { - throw new IllegalArgumentException("Sandbox configuration cannot be null"); - } - - SandboxType sandboxType = config.getSandboxType(); - - classRegistry.put(targetClass, config); - typeRegistry.put(sandboxType, targetClass); - typeConfigRegistry.put(sandboxType, config); - - logger.info("Registered sandbox: type={}, class={}, image={}", sandboxType, targetClass.getSimpleName(), config.getImageName()); - } - - /** - * Register a sandbox configuration by type - * This is a simplified registration method that doesn't require a class - * - * @param sandboxType The sandbox type - * @param imageName The Docker image name - */ - public static void register(SandboxType sandboxType, String imageName) { - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(sandboxType) - .imageName(imageName) - .build(); - typeConfigRegistry.put(sandboxType, config); - - logger.info("Registered sandbox: type={}, image={}", sandboxType, imageName); - } - - /** - * Register a sandbox with full configuration - * - * @param sandboxType The sandbox type - * @param imageName The Docker image name - * @param resourceLimits Resource limits - * @param securityLevel Security level - * @param timeout Timeout in seconds - * @param description Description - * @param environment Environment variables - * @param runtimeConfig Runtime configuration - */ - public static void register( - SandboxType sandboxType, - String imageName, - Map resourceLimits, - String securityLevel, - int timeout, - String description, - Map environment, - Map runtimeConfig) { - - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(sandboxType) - .imageName(imageName) - .resourceLimits(resourceLimits) - .securityLevel(securityLevel) - .timeout(timeout) - .description(description) - .environment(environment) - .runtimeConfig(runtimeConfig) - .build(); - - typeConfigRegistry.put(sandboxType, config); - - logger.info("Registered sandbox with full config: type={}, image={}, timeout={}s", sandboxType, imageName, timeout); - } - - /** - * Get sandbox configuration by class - * - * @param targetClass The target class - * @return Optional containing the configuration if found - */ - public static Optional getConfig(Class targetClass) { - return Optional.ofNullable(classRegistry.get(targetClass)); - } - - /** - * Get sandbox configuration by type - * - * @param sandboxType The sandbox type - * @return Optional containing the configuration if found - */ - public static Optional getConfigByType(SandboxType sandboxType) { - return Optional.ofNullable(typeConfigRegistry.get(sandboxType)); - } - - /** - * Get Docker image name by class - * - * @param targetClass The target class - * @return Optional containing the image name if found - */ - public static Optional getImage(Class targetClass) { - return getConfig(targetClass).map(SandboxConfig::getImageName); - } - - /** - * Get Docker image name by sandbox type - * - * @param sandboxType The sandbox type - * @return Optional containing the image name if found - */ - public static Optional getImageByType(SandboxType sandboxType) { - return getConfigByType(sandboxType).map(SandboxConfig::getImageName); - } - - /** - * Get class by sandbox type - * - * @param sandboxType The sandbox type - * @return Optional containing the class if found - */ - public static Optional> getClassesByType(SandboxType sandboxType) { - return Optional.ofNullable(typeRegistry.get(sandboxType)); - } - - /** - * List all registered sandboxes (class-based registrations) - * - * @return A copy of the registry - */ - public static Map, SandboxConfig> listAllSandboxes() { - return new HashMap<>(classRegistry); - } - - /** - * List all registered sandbox configurations by type - * - * @return A copy of the type-based registry - */ - public static Map listAllSandboxesByType() { - return new HashMap<>(typeConfigRegistry); - } - - /** - * Check if a sandbox type is registered - * - * @param sandboxType The sandbox type to check - * @return true if registered, false otherwise - */ - public static boolean isRegistered(SandboxType sandboxType) { - return typeConfigRegistry.containsKey(sandboxType); - } - - /** - * Unregister a sandbox type - * - * @param sandboxType The sandbox type to unregister - * @return true if unregistered successfully, false if not found - */ - public static boolean unregister(SandboxType sandboxType) { - SandboxConfig config = typeConfigRegistry.remove(sandboxType); - Class clazz = typeRegistry.remove(sandboxType); - if (clazz != null) { - classRegistry.remove(clazz); - } - - if (config != null) { - logger.info("Unregistered sandbox: type={}", sandboxType); - return true; - } - return false; - } - - /** - * Clear all registrations - */ - public static void clear() { - classRegistry.clear(); - typeRegistry.clear(); - typeConfigRegistry.clear(); - customTypeRegistry.clear(); - logger.info("Cleared all sandbox registrations"); - } - - /** - * Get the count of registered sandboxes - * - * @return Number of registered sandbox types - */ - public static int getRegisteredCount() { - return typeConfigRegistry.size(); - } - - /** - * Register a custom sandbox type by string name - * This supports dynamic type registration. - * - * @param typeName The custom type name - * @param imageName The Docker image name - */ - public static void registerCustomType(String typeName, String imageName) { - DynamicSandboxType.custom(typeName); - - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(SandboxType.BASE) // Placeholder, actual type is identified by string name - .imageName(imageName) - .build(); - - customTypeRegistry.put(typeName.toLowerCase(), config); - logger.info("Registered custom sandbox type: name={}, image={}", typeName, imageName); - } - - /** - * Register a custom sandbox type with full configuration - * - * @param typeName The custom type name - * @param imageName The Docker image name - * @param resourceLimits Resource limits - * @param securityLevel Security level - * @param timeout Timeout in seconds - * @param description Description - * @param environment Environment variables - * @param runtimeConfig Runtime configuration - */ - public static void registerCustomType( - String typeName, - String imageName, - Map resourceLimits, - String securityLevel, - int timeout, - String description, - Map environment, - Map runtimeConfig) { - - DynamicSandboxType.custom(typeName); - - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(SandboxType.BASE) // Placeholder - .imageName(imageName) - .resourceLimits(resourceLimits) - .securityLevel(securityLevel) - .timeout(timeout) - .description(description) - .environment(environment) - .runtimeConfig(runtimeConfig) - .build(); - - customTypeRegistry.put(typeName.toLowerCase(), config); - - logger.info("Registered custom sandbox type with full config: name={}, image={}, timeout={}s", typeName, imageName, timeout); - } - - /** - * Get configuration for a custom sandbox type by name - * - * @param typeName The custom type name - * @return Optional containing the configuration if found - */ - public static Optional getCustomTypeConfig(String typeName) { - if (typeName == null) { - return Optional.empty(); - } - return Optional.ofNullable(customTypeRegistry.get(typeName.toLowerCase())); - } - - /** - * Get image name for a custom sandbox type - * - * @param typeName The custom type name - * @return Optional containing the image name if found - */ - public static Optional getCustomTypeImage(String typeName) { - return getCustomTypeConfig(typeName).map(SandboxConfig::getImageName); - } - - /** - * Check if a custom type is registered - * - * @param typeName The custom type name - * @return true if registered, false otherwise - */ - public static boolean isCustomTypeRegistered(String typeName) { - if (typeName == null) { - return false; - } - return customTypeRegistry.containsKey(typeName.toLowerCase()); - } - - /** - * List all registered custom sandbox types - * - * @return A copy of the custom type registry - */ - public static Map listAllCustomTypes() { - return new HashMap<>(customTypeRegistry); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryService.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryService.java deleted file mode 100644 index 3f760a4f..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryService.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.registry; - -import io.agentscope.runtime.sandbox.manager.model.container.DynamicSandboxType; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxConfig; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Sandbox registry for managing sandbox configurations - */ -public class SandboxRegistryService { - private static final Logger logger = LoggerFactory.getLogger(SandboxRegistryService.class); - - private static final Map, SandboxConfig> classRegistry = new ConcurrentHashMap<>(); - private static final Map> typeRegistry = new ConcurrentHashMap<>(); - private static final Map typeConfigRegistry = new ConcurrentHashMap<>(); - - private static final Map customTypeRegistry = new ConcurrentHashMap<>(); - - static { - try { - Class.forName(SandboxRegistryInitializer.class.getName()); - logger.info("SandboxRegistryInitializer loaded and executed"); - } catch (ClassNotFoundException e) { - logger.warn("SandboxRegistryInitializer not found, annotation-based registration disabled"); - } - } - - /** - * Register a sandbox configuration for a specific class and type - * - * @param targetClass The class to register - * @param config The sandbox configuration - */ - public static void register(Class targetClass, SandboxConfig config) { - if (targetClass == null) { - throw new IllegalArgumentException("Target class cannot be null"); - } - if (config == null) { - throw new IllegalArgumentException("Sandbox configuration cannot be null"); - } - - SandboxType sandboxType = config.getSandboxType(); - - classRegistry.put(targetClass, config); - typeRegistry.put(sandboxType, targetClass); - typeConfigRegistry.put(sandboxType, config); - - logger.info("Registered sandbox: type={}, class={}, image={}", sandboxType, targetClass.getSimpleName(), config.getImageName()); - } - - /** - * Register a sandbox configuration by type - * This is a simplified registration method that doesn't require a class - * - * @param sandboxType The sandbox type - * @param imageName The Docker image name - */ - public static void register(SandboxType sandboxType, String imageName) { - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(sandboxType) - .imageName(imageName) - .build(); - typeConfigRegistry.put(sandboxType, config); - - logger.info("Registered sandbox: type={}, image={}", sandboxType, imageName); - } - - /** - * Register a sandbox with full configuration - * - * @param sandboxType The sandbox type - * @param imageName The Docker image name - * @param resourceLimits Resource limits - * @param securityLevel Security level - * @param timeout Timeout in seconds - * @param description Description - * @param environment Environment variables - * @param runtimeConfig Runtime configuration - */ - public static void register( - SandboxType sandboxType, - String imageName, - Map resourceLimits, - String securityLevel, - int timeout, - String description, - Map environment, - Map runtimeConfig) { - - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(sandboxType) - .imageName(imageName) - .resourceLimits(resourceLimits) - .securityLevel(securityLevel) - .timeout(timeout) - .description(description) - .environment(environment) - .runtimeConfig(runtimeConfig) - .build(); - - typeConfigRegistry.put(sandboxType, config); - - logger.info("Registered sandbox with full config: type={}, image={}, timeout={}s", sandboxType, imageName, timeout); - } - - /** - * Get sandbox configuration by class - * - * @param targetClass The target class - * @return Optional containing the configuration if found - */ - public static Optional getConfig(Class targetClass) { - return Optional.ofNullable(classRegistry.get(targetClass)); - } - - /** - * Get sandbox configuration by type - * - * @param sandboxType The sandbox type - * @return Optional containing the configuration if found - */ - public static Optional getConfigByType(SandboxType sandboxType) { - return Optional.ofNullable(typeConfigRegistry.get(sandboxType)); - } - - /** - * Get Docker image name by class - * - * @param targetClass The target class - * @return Optional containing the image name if found - */ - public static Optional getImage(Class targetClass) { - return getConfig(targetClass).map(SandboxConfig::getImageName); - } - - /** - * Get Docker image name by sandbox type - * - * @param sandboxType The sandbox type - * @return Optional containing the image name if found - */ - public static Optional getImageByType(SandboxType sandboxType) { - return getConfigByType(sandboxType).map(SandboxConfig::getImageName); - } - - /** - * Get class by sandbox type - * - * @param sandboxType The sandbox type - * @return Optional containing the class if found - */ - public static Optional> getClassesByType(SandboxType sandboxType) { - return Optional.ofNullable(typeRegistry.get(sandboxType)); - } - - /** - * List all registered sandboxes (class-based registrations) - * - * @return A copy of the registry - */ - public static Map, SandboxConfig> listAllSandboxes() { - return new HashMap<>(classRegistry); - } - - /** - * List all registered sandbox configurations by type - * - * @return A copy of the type-based registry - */ - public static Map listAllSandboxesByType() { - return new HashMap<>(typeConfigRegistry); - } - - /** - * Check if a sandbox type is registered - * - * @param sandboxType The sandbox type to check - * @return true if registered, false otherwise - */ - public static boolean isRegistered(SandboxType sandboxType) { - return typeConfigRegistry.containsKey(sandboxType); - } - - /** - * Unregister a sandbox type - * - * @param sandboxType The sandbox type to unregister - * @return true if unregistered successfully, false if not found - */ - public static boolean unregister(SandboxType sandboxType) { - SandboxConfig config = typeConfigRegistry.remove(sandboxType); - Class clazz = typeRegistry.remove(sandboxType); - if (clazz != null) { - classRegistry.remove(clazz); - } - - if (config != null) { - logger.info("Unregistered sandbox: type={}", sandboxType); - return true; - } - return false; - } - - /** - * Clear all registrations - */ - public static void clear() { - classRegistry.clear(); - typeRegistry.clear(); - typeConfigRegistry.clear(); - customTypeRegistry.clear(); - logger.info("Cleared all sandbox registrations"); - } - - /** - * Get the count of registered sandboxes - * - * @return Number of registered sandbox types - */ - public static int getRegisteredCount() { - return typeConfigRegistry.size(); - } - - /** - * Register a custom sandbox type by string name - * This supports dynamic type registration. - * - * @param typeName The custom type name - * @param imageName The Docker image name - */ - public static void registerCustomType(String typeName, String imageName) { - DynamicSandboxType.custom(typeName); - - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(SandboxType.BASE) // Placeholder, actual type is identified by string name - .imageName(imageName) - .build(); - - customTypeRegistry.put(typeName.toLowerCase(), config); - logger.info("Registered custom sandbox type: name={}, image={}", typeName, imageName); - } - - /** - * Register a custom sandbox type with full configuration - * - * @param typeName The custom type name - * @param imageName The Docker image name - * @param resourceLimits Resource limits - * @param securityLevel Security level - * @param timeout Timeout in seconds - * @param description Description - * @param environment Environment variables - * @param runtimeConfig Runtime configuration - */ - public static void registerCustomType( - String typeName, - String imageName, - Map resourceLimits, - String securityLevel, - int timeout, - String description, - Map environment, - Map runtimeConfig) { - - // Create dynamic type to ensure it's registered in the type system - DynamicSandboxType.custom(typeName); - - // Create full config for the custom type - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(SandboxType.BASE) // Placeholder - .imageName(imageName) - .resourceLimits(resourceLimits) - .securityLevel(securityLevel) - .timeout(timeout) - .description(description) - .environment(environment) - .runtimeConfig(runtimeConfig) - .build(); - - customTypeRegistry.put(typeName.toLowerCase(), config); - - logger.info("Registered custom sandbox type with full config: name={}, image={}, timeout={}s", typeName, imageName, timeout); - } - - /** - * Get configuration for a custom sandbox type by name - * - * @param typeName The custom type name - * @return Optional containing the configuration if found - */ - public static Optional getCustomTypeConfig(String typeName) { - if (typeName == null) { - return Optional.empty(); - } - return Optional.ofNullable(customTypeRegistry.get(typeName.toLowerCase())); - } - - /** - * Get image name for a custom sandbox type - * - * @param typeName The custom type name - * @return Optional containing the image name if found - */ - public static Optional getCustomTypeImage(String typeName) { - return getCustomTypeConfig(typeName).map(SandboxConfig::getImageName); - } - - /** - * Check if a custom type is registered - * - * @param typeName The custom type name - * @return true if registered, false otherwise - */ - public static boolean isCustomTypeRegistered(String typeName) { - if (typeName == null) { - return false; - } - return customTypeRegistry.containsKey(typeName.toLowerCase()); - } - - /** - * List all registered custom sandbox types - * - * @return A copy of the custom type registry - */ - public static Map listAllCustomTypes() { - return new HashMap<>(customTypeRegistry); - } -} - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/HttpClient.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/HttpClient.java deleted file mode 100644 index 6e49ae29..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/HttpClient.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Map; - -/** - * HTTP client utility class for sending HTTP requests - */ -public class HttpClient { - - private final CloseableHttpClient httpClient; - private final ObjectMapper objectMapper; - - public HttpClient() { - this.httpClient = HttpClients.createDefault(); - this.objectMapper = new ObjectMapper(); - } - - /** - * Send POST request - * - * @param url request URL - * @param headers request headers - * @param requestBody request body - * @return response content - * @throws IOException request exception - */ - public String post(String url, Map headers, String requestBody) throws IOException { - HttpPost httpPost = new HttpPost(url); - - // Set request headers - if (headers != null) { - headers.forEach(httpPost::setHeader); - } - - // Set request body - if (requestBody != null) { - StringEntity entity = new StringEntity(requestBody, ContentType.APPLICATION_JSON); - httpPost.setEntity(entity); - } - - try (CloseableHttpResponse response = httpClient.execute(httpPost)) { - int statusCode = response.getStatusLine().getStatusCode(); - String responseBody = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8); - - if (statusCode < 200 || statusCode >= 300) { - throw new IOException("HTTP request failed, status code: " + statusCode + ", response: " + responseBody); - } - return responseBody; - } - } - - /** - * Send JSON POST request - * - * @param url request URL - * @param headers request headers - * @param requestData request data object - * @return response content - * @throws IOException request exception - */ - public String postJson(String url, Map headers, Object requestData) throws IOException { - String jsonBody = objectMapper.writeValueAsString(requestData); - return post(url, headers, jsonBody); - } - - /** - * Send GET request - * - * @param url request URL - * @param headers request headers - * @return response content - * @throws IOException request exception - */ - public String get(String url, Map headers) throws IOException { - HttpGet httpGet = new HttpGet(url); - if (headers != null) { - headers.forEach(httpGet::setHeader); - } - try (CloseableHttpResponse response = httpClient.execute(httpGet)) { - int statusCode = response.getStatusLine().getStatusCode(); - String responseBody = response.getEntity() != null ? new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8) : ""; - if (statusCode < 200 || statusCode >= 300) { - throw new IOException("HTTP request failed, status code: " + statusCode + ", response: " + responseBody); - } - return responseBody; - } - } - - /** - * Close HTTP client - */ - public void close() throws IOException { - if (httpClient != null) { - httpClient.close(); - } - } -} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/StorageManager.java b/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/StorageManager.java deleted file mode 100644 index 09cac0b7..00000000 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/StorageManager.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager.util; - -import com.aliyun.oss.OSS; -import com.aliyun.oss.OSSClientBuilder; -import com.aliyun.oss.model.ListObjectsRequest; -import com.aliyun.oss.model.OSSObject; -import com.aliyun.oss.model.OSSObjectSummary; -import com.aliyun.oss.model.ObjectListing; -import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemConfig; -import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemType; -import io.agentscope.runtime.sandbox.manager.model.fs.OssConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -/** - * Storage Manager responsible for handling file downloads from local and cloud storage - */ -public class StorageManager { - private static final Logger logger = LoggerFactory.getLogger(StorageManager.class); - - private final FileSystemConfig fileSystemConfig; - private OSS ossClient; - - public StorageManager(FileSystemConfig fileSystemConfig) { - this.fileSystemConfig = fileSystemConfig; - - // If OSS storage, initialize OSS client - if (fileSystemConfig.getFileSystemType() == FileSystemType.OSS && fileSystemConfig instanceof OssConfig ossConfig) { - this.ossClient = new OSSClientBuilder().build( - ossConfig.getOssEndpoint(), - ossConfig.getOssAccessKeyId(), - ossConfig.getOssAccessKeySecret() - ); - logger.info("OSS client initialized with endpoint: {}", ossConfig.getOssEndpoint()); - } - } - - /** - * Path join - * - * @param parts path components - * @return joined path - */ - public String pathJoin(String... parts) { - if (parts == null || parts.length == 0) { - return ""; - } - - Path path = Paths.get(parts[0]); - for (int i = 1; i < parts.length; i++) { - path = path.resolve(parts[i]); - } - - return path.toString(); - } - - /** - * Download folder from storage to local directory - * - * @param storagePath storage path (OSS object prefix or local path) - * @param localDir local target directory - * @return whether download succeeded - */ - public boolean downloadFolder(String storagePath, String localDir) { - if (storagePath == null || storagePath.isEmpty()) { - logger.warn("Storage path is empty, skipping download"); - return false; - } - - if (localDir == null || localDir.isEmpty()) { - logger.warn("Local directory is empty, skipping download"); - return false; - } - - try { - if (fileSystemConfig.getFileSystemType() == FileSystemType.OSS) { - return downloadFromOss(storagePath, localDir); - } else { - // LOCAL type: copy from local path to target path - return copyLocalFolder(storagePath, localDir); - } - } catch (Exception e) { - logger.error("Failed to download folder from {} to {}: {}", storagePath, localDir, e.getMessage()); - return false; - } - } - - /** - * Download folder from OSS - * - * @param ossPrefix OSS object prefix - * @param localDir local target directory - * @return whether download succeeded - */ - private boolean downloadFromOss(String ossPrefix, String localDir) { - if (ossClient == null || !(fileSystemConfig instanceof OssConfig ossConfig)) { - logger.error("OSS client not initialized"); - return false; - } - - try { - // Ensure local directory exists - File localDirectory = new File(localDir); - if (!localDirectory.exists()) { - localDirectory.mkdirs(); - } - - String bucketName = ossConfig.getOssBucketName(); - - // Ensure prefix ends with / - String prefix = ossPrefix.endsWith("/") ? ossPrefix : ossPrefix + "/"; - - logger.info("Downloading from OSS bucket: {}, prefix: {} to {}", bucketName, prefix, localDir); - - // List all objects - ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName) - .withPrefix(prefix) - .withMaxKeys(1000); - - ObjectListing objectListing; - int downloadedCount = 0; - - do { - objectListing = ossClient.listObjects(listObjectsRequest); - - for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) { - String objectKey = objectSummary.getKey(); - - // Skip directory marker objects - if (objectKey.endsWith("/")) { - continue; - } - - // Calculate relative path - String relativePath = objectKey.substring(prefix.length()); - File localFile = new File(localDir, relativePath); - - // Ensure parent directory exists - File parentDir = localFile.getParentFile(); - if (parentDir != null && !parentDir.exists()) { - parentDir.mkdirs(); - } - - // Download file - try { - OSSObject ossObject = ossClient.getObject(bucketName, objectKey); - try (InputStream inputStream = ossObject.getObjectContent(); - FileOutputStream outputStream = new FileOutputStream(localFile)) { - byte[] buffer = new byte[8192]; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - } - } - downloadedCount++; - logger.info("Downloaded: {} to {}", objectKey, localFile.getAbsolutePath()); - } catch (Exception e) { - logger.warn("Failed to download {}: {}", objectKey, e.getMessage()); - } - } - - listObjectsRequest.setMarker(objectListing.getNextMarker()); - } while (objectListing.isTruncated()); - - logger.info("Downloaded {} files from OSS", downloadedCount); - return true; - - } catch (Exception e) { - logger.error("Failed to download from OSS: {}", e.getMessage()); - return false; - } - } - - /** - * Copy folder from local path - * - * @param sourcePath source path - * @param targetPath target path - * @return whether copy succeeded - */ - private boolean copyLocalFolder(String sourcePath, String targetPath) { - try { - Path source = Paths.get(sourcePath); - Path target = Paths.get(targetPath); - - if (!Files.exists(source)) { - logger.warn("Source path does not exist: {}", sourcePath); - return false; - } - - // Ensure target directory exists - if (!Files.exists(target)) { - Files.createDirectories(target); - } - - // If source is directory, copy recursively - if (Files.isDirectory(source)) { - Files.walk(source) - .forEach(srcPath -> { - try { - Path destPath = target.resolve(source.relativize(srcPath)); - if (Files.isDirectory(srcPath)) { - if (!Files.exists(destPath)) { - Files.createDirectories(destPath); - } - } else { - Files.copy(srcPath, destPath, StandardCopyOption.REPLACE_EXISTING); - } - } catch (IOException e) { - logger.warn("Failed to copy {}: {}", srcPath, e.getMessage()); - } - }); - } else { - // If source is file, copy directly - Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); - } - - logger.info("Copied folder from {} to {}", sourcePath, targetPath); - return true; - - } catch (Exception e) { - logger.error("Failed to copy local folder: {}", e.getMessage()); - return false; - } - } - - /** - * Upload local folder to storage - * - * @param localDir local source directory - * @param storagePath storage path (OSS object prefix or local path) - * @return whether upload succeeded - */ - public boolean uploadFolder(String localDir, String storagePath) { - if (localDir == null || localDir.isEmpty()) { - logger.warn("Local directory is empty, skipping upload"); - return false; - } - - if (storagePath == null || storagePath.isEmpty()) { - logger.warn("Storage path is empty, skipping upload"); - return false; - } - - // Check if local directory exists - File localDirectory = new File(localDir); - if (!localDirectory.exists()) { - logger.warn("Local directory does not exist: {}", localDir); - return false; - } - - try { - if (fileSystemConfig.getFileSystemType() == FileSystemType.OSS) { - return uploadToOss(localDir, storagePath); - } else { - return copyLocalFolder(localDir, storagePath); - } - } catch (Exception e) { - logger.error("Failed to upload folder from {} to {}: {}", localDir, storagePath, e.getMessage()); - return false; - } - } - - /** - * Upload local folder to OSS - * - * @param localDir local source directory - * @param ossPrefix OSS object prefix - * @return whether upload succeeded - */ - private boolean uploadToOss(String localDir, String ossPrefix) { - if (ossClient == null || !(fileSystemConfig instanceof OssConfig ossConfig)) { - logger.error("OSS client not initialized"); - return false; - } - - try { - File localDirectory = new File(localDir); - if (!localDirectory.exists() || !localDirectory.isDirectory()) { - logger.warn("Local directory does not exist or is not a directory: {}", localDir); - return false; - } - - String bucketName = ossConfig.getOssBucketName(); - - // Ensure prefix ends with / - String prefix = ossPrefix.endsWith("/") ? ossPrefix : ossPrefix + "/"; - - logger.info("Uploading to OSS bucket: {}, prefix: {} from {}", bucketName, prefix, localDir); - - int uploadedCount = 0; - uploadedCount = uploadDirectoryToOss(localDirectory, bucketName, prefix, ""); - - logger.info("Uploaded {} files to OSS", uploadedCount); - return true; - - } catch (Exception e) { - logger.error("Failed to upload to OSS: {}", e.getMessage()); - return false; - } - } - - /** - * Recursively upload directory to OSS - * - * @param directory current directory - * @param bucketName OSS bucket name - * @param basePrefix base prefix - * @param relativePath relative path - * @return number of files uploaded - */ - private int uploadDirectoryToOss(File directory, String bucketName, String basePrefix, String relativePath) { - int uploadedCount = 0; - - File[] files = directory.listFiles(); - if (files == null) { - return 0; - } - - for (File file : files) { - String currentRelativePath = relativePath.isEmpty() ? file.getName() : relativePath + "/" + file.getName(); - - if (file.isDirectory()) { - // Recursively upload subdirectories - uploadedCount += uploadDirectoryToOss(file, bucketName, basePrefix, currentRelativePath); - } else { - // Upload file - String objectKey = basePrefix + currentRelativePath; - - try { - // Upload file to OSS - ossClient.putObject(bucketName, objectKey, file); - uploadedCount++; - logger.info("Uploaded: {} to {}", file.getAbsolutePath(), objectKey); - } catch (Exception e) { - logger.warn("Failed to upload {} to {}: {}", file.getAbsolutePath(), objectKey, e.getMessage()); - } - } - } - - return uploadedCount; - } - - /** - * Close OSS client - */ - public void close() { - if (ossClient != null) { - try { - ossClient.shutdown(); - logger.info("OSS client closed"); - } catch (Exception e) { - logger.warn("Failed to close OSS client: {}", e.getMessage()); - } - } - } -} - diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/ContainerPoolTest.java b/core/src/test/java/io/agentscope/runtime/sandbox/manager/ContainerPoolTest.java deleted file mode 100644 index 9712b09d..00000000 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/ContainerPoolTest.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.agentscope.runtime.sandbox.manager; - -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.condition.EnabledIf; -import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Container Pool Functionality Test - * - *

Notes: - *

    - *
  • Each test creates and destroys multiple Docker containers
  • - *
  • There are appropriate delays between tests to ensure ports and resources are fully released
  • - *
  • All containers are automatically cleaned up after tests
  • - *
  • If port conflicts occur, SandboxManager will automatically retry
  • - *
- */ -@EnabledIfDockerAvailable -@EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") -public class ContainerPoolTest { - private static boolean isCI() { - return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); - } - - private SandboxManager manager; - - @BeforeEach - void setUp() { - // Ensure a clean state at the start of each test - manager = null; - System.out.println("Starting new test..."); - } - - @AfterEach - void tearDown() { - if (manager != null) { - try { - System.out.println("Cleaning up test resources..."); - manager.cleanupAllSandboxes(); - manager.close(); - System.out.println("Pool cleaned up successfully"); - - // Wait a short period to ensure ports are fully released - // This avoids port conflicts between rapidly consecutive tests - Thread.sleep(500); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - System.err.println("Cleanup interrupted: " + e.getMessage()); - } catch (Exception e) { - System.err.println("Error during cleanup: " + e.getMessage()); - e.printStackTrace(); - } finally { - manager = null; - } - } - } - - /** - * Create SandboxManager with container pool - */ - @Test - void testCreateSandboxManagerWithPool() { - System.out.println("\n--- Test Create SandboxManager with Pool ---"); - - // Create configuration with pool size - ManagerConfig config = new ManagerConfig.Builder() - .poolSize(3) // Pre-create 3 containers - .build(); - - System.out.println("Creating SandboxManager with pool size = 3"); - System.out.println("This will pre-create 3 containers (similar to Python's pool_size)"); - - // Create manager - this will automatically initialize the pool - manager = new SandboxManager(config); - manager.start(); - - assertNotNull(manager, "SandboxManager should be created successfully"); - - System.out.println("\nSandboxManager created!"); - System.out.println("Container pool has been initialized with pre-warmed containers"); - System.out.println("These containers are ready to be used immediately"); - - // Explicitly clean up pool containers - System.out.println("\n--- Cleaning up container pool ---"); - manager.cleanupAllSandboxes(); - System.out.println("✓ Container pool (3 containers) cleaned up successfully"); - } - - /** - * Pool behavior with different types - */ - @Test - void testPoolBehaviorWithDifferentTypes() { - System.out.println("\n--- Test Mixed Container Types ---"); - - ManagerConfig config = new ManagerConfig.Builder() - .poolSize(2) // Pool only contains BASE type - .build(); - - manager = new SandboxManager(config); - manager.start(); - assertNotNull(manager, "SandboxManager should be created successfully"); - - // Define sessionIDs - String baseSessionID = "test-session-base"; - String browserSessionID = "test-session-browser"; - - // Get BASE type from pool - fast - System.out.println("Getting BASE type (from pool)..."); - ContainerModel baseContainer = manager.createFromPool(SandboxType.BASE, "test-user", baseSessionID); - assertNotNull(baseContainer, "BASE container should be created successfully"); - System.out.println("Got: " + baseContainer.getContainerName()); - - // Get BROWSER type - will be created directly (not from pool) - System.out.println("\nGetting BROWSER type (direct creation)..."); - ContainerModel browserContainer = manager.createFromPool(SandboxType.BROWSER, "test-user", browserSessionID); - assertNotNull(browserContainer, "BROWSER container should be created successfully"); - System.out.println("Got: " + browserContainer.getContainerName()); - System.out.println("Note: BROWSER was created directly since pool only has BASE type"); - - // Explicitly release containers - System.out.println("\n--- Cleaning up containers ---"); - System.out.println("BASE container: " + baseContainer.getContainerName() + " (ID: " + baseContainer.getContainerId() + ")"); - System.out.println(" - Random sessionId: " + baseContainer.getSessionId()); - System.out.println("BROWSER container: " + browserContainer.getContainerName() + " (ID: " + browserContainer.getContainerId() + ")"); - System.out.println(" - Random sessionId: " + browserContainer.getSessionId()); - - // Release containers using their random sessionId - System.out.println("\nReleasing containers using their random sessionId..."); - boolean baseReleased = manager.release(baseContainer.getSessionId()); - System.out.println("BASE container released: " + baseReleased); - assertTrue(baseReleased, "BASE container should be released successfully"); - - boolean browserReleased = manager.release(browserContainer.getSessionId()); - System.out.println("BROWSER container released: " + browserReleased); - assertTrue(browserReleased, "BROWSER container should be released successfully"); - - // Clean up remaining pool containers - System.out.println("\nCleaning up remaining pool containers..."); - manager.cleanupAllSandboxes(); - System.out.println("✓ All containers cleaned up successfully"); - } - - /** - * Verify port management across multiple container creations - */ - @Test - void testPortManagementAcrossMultipleCreations() { - System.out.println("\n--- Test Port Management Across Multiple Creations ---"); - - ManagerConfig config = new ManagerConfig.Builder() - .poolSize(0) - .build(); - - manager = new SandboxManager(config); - manager.start(); - assertNotNull(manager, "SandboxManager should be created successfully"); - - // Define sessionIDs - String sessionID1 = "test-port-session-1"; - String sessionID2 = "test-port-session-2"; - String sessionID3 = "test-port-session-3"; - - // Quickly create multiple containers in succession - System.out.println("Creating 3 containers in quick succession..."); - ContainerModel container1 = manager.createFromPool(SandboxType.BASE, "test-user-1", sessionID1); - assertNotNull(container1, "Container 1 should be created successfully"); - System.out.println("Created container 1: " + container1.getContainerName()); - - ContainerModel container2 = manager.createFromPool(SandboxType.BASE, "test-user-2", sessionID2); - assertNotNull(container2, "Container 2 should be created successfully"); - System.out.println("Created container 2: " + container2.getContainerName()); - - ContainerModel container3 = manager.createFromPool(SandboxType.BASE, "test-user-3", sessionID3); - assertNotNull(container3, "Container 3 should be created successfully"); - System.out.println("Created container 3: " + container3.getContainerName()); - - // Verify they all have different IDs - assertNotEquals(container1.getContainerId(), container2.getContainerId(), - "Containers should have different IDs"); - assertNotEquals(container2.getContainerId(), container3.getContainerId(), - "Containers should have different IDs"); - assertNotEquals(container1.getContainerId(), container3.getContainerId(), - "Containers should have different IDs"); - - System.out.println("\nAll containers created successfully with unique IDs"); - System.out.println("Port management is working correctly"); - - // Explicitly release all containers using their random sessionIds - System.out.println("\n--- Cleaning up containers ---"); - System.out.println("Container 1: " + container1.getContainerName() + " (ID: " + container1.getContainerId() + ")"); - System.out.println(" Random sessionId: " + container1.getSessionId()); - System.out.println("Container 2: " + container2.getContainerName() + " (ID: " + container2.getContainerId() + ")"); - System.out.println(" Random sessionId: " + container2.getSessionId()); - System.out.println("Container 3: " + container3.getContainerName() + " (ID: " + container3.getContainerId() + ")"); - System.out.println(" Random sessionId: " + container3.getSessionId()); - - System.out.println("\nReleasing containers using their random sessionId..."); - boolean released1 = manager.release(container1.getSessionId()); - System.out.println("Container 1 released: " + released1); - assertTrue(released1, "Container 1 should be released successfully"); - - boolean released2 = manager.release(container2.getSessionId()); - System.out.println("Container 2 released: " + released2); - assertTrue(released2, "Container 2 should be released successfully"); - - boolean released3 = manager.release(container3.getSessionId()); - System.out.println("Container 3 released: " + released3); - assertTrue(released3, "Container 3 should be released successfully"); - - // Clean up pool - System.out.println("\nCleaning up pool..."); - manager.cleanupAllSandboxes(); - System.out.println("All 3 containers cleaned up successfully"); - } -} diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/CustomSandboxTest.java b/core/src/test/java/io/agentscope/runtime/sandbox/manager/CustomSandboxTest.java deleted file mode 100644 index d99152f4..00000000 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/CustomSandboxTest.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager; - -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; -import io.agentscope.runtime.sandbox.manager.registry.SandboxAnnotationProcessor; -import io.agentscope.runtime.sandbox.manager.registry.SandboxRegistryInitializer; -import io.agentscope.runtime.sandbox.manager.registry.SandboxRegistryService; -import org.junit.Ignore; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIf; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Custom Sandbox Test Class - * Demonstrates how to create and test custom sandboxes using @RegisterSandbox annotation - */ -@Ignore -@DisplayName("Custom Sandbox Registration and Configuration Tests") -@EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") -public class CustomSandboxTest { - - private static boolean isCI() { - return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); - } - - @RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-python:latest", - sandboxType = SandboxType.BASE, - securityLevel = "high", - timeout = 60, - description = "Custom Python Sandbox with extra security", - resourceLimits = {"memory=2g", "cpu=2.0"} - ) - public static class CustomPythonSandbox extends Sandbox { - public CustomPythonSandbox( - SandboxManager managerApi, - String userId, - String sessionId) { - super(managerApi, userId, sessionId, SandboxType.BASE, 60); - } - - public String executePython(String code) { - Map arguments = new HashMap<>(); - arguments.put("code", code); - return callTool("run_ipython_cell", arguments); - } - } - - @RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-custom_sandbox:latest", - customType = "custom_sandbox", - securityLevel = "medium", - timeout = 60, - description = "my sandbox", - environment = { - "TAVILY_API_KEY=${TAVILY_API_KEY}", - "AMAP_MAPS_API_KEY=${AMAP_MAPS_API_KEY}" - } - ) - public static class MyCustomSandbox extends Sandbox { - public MyCustomSandbox( - SandboxManager managerApi, - String userId, - String sessionId) { - super(managerApi, userId, sessionId, SandboxType.BASE, 60); - } - } - - @RegisterSandbox( - imageName = "my-registry/my-advanced-sandbox:latest", - customType = "advanced_sandbox", - securityLevel = "high", - timeout = 120, - description = "Advanced custom sandbox with full configuration", - environment = { - "API_KEY=${API_KEY}", - "DEBUG_MODE=true", - "MAX_WORKERS=4" - }, - resourceLimits = { - "memory=4g", - "cpu=4.0" - }, - runtimeConfig = { - "enable_gpu=true", - "max_connections=100" - } - ) - public static class AdvancedCustomSandbox extends Sandbox { - public AdvancedCustomSandbox( - SandboxManager managerApi, - String userId, - String sessionId) { - super(managerApi, userId, sessionId, SandboxType.BASE, 120); - } - - public String executeAdvancedTask(String taskName, Map params) { - Map arguments = new HashMap<>(); - arguments.put("task", taskName); - arguments.putAll(params); - return callTool("execute_task", arguments); - } - } - - @BeforeAll - @DisplayName("Initialize and register all custom sandboxes") - public static void setUp() { - System.out.println("\n=== Initializing Custom Sandbox Tests ===\n"); - System.out.println("Step 1: Initializing Sandbox Registry..."); - SandboxRegistryInitializer.initialize(); - System.out.println("Step 2: Registering custom sandbox classes..."); - SandboxAnnotationProcessor.processClass(CustomPythonSandbox.class); - SandboxAnnotationProcessor.processClass(MyCustomSandbox.class); - SandboxAnnotationProcessor.processClass(AdvancedCustomSandbox.class); - System.out.println("Initialization complete\n"); - } - - @Test - @DisplayName("Test CustomPythonSandbox registration") - public void testCustomPythonSandboxRegistration() { - System.out.println("\n--- Test CustomPythonSandbox Registration ---"); - - boolean pythonRegistered = SandboxRegistryService.isRegistered(SandboxType.BASE); - assertTrue(pythonRegistered, "CustomPythonSandbox should be registered"); - System.out.println("✓ CustomPythonSandbox registered"); - - SandboxRegistryService.getConfigByType(SandboxType.BASE).ifPresent(config -> { - System.out.println("\nConfiguration:"); - System.out.println(" Image: " + config.getImageName()); - System.out.println(" Type: " + config.getSandboxType()); - System.out.println(" Security Level: " + config.getSecurityLevel()); - System.out.println(" Timeout: " + config.getTimeout() + "s"); - System.out.println(" Description: " + config.getDescription()); - System.out.println(" Resource Limits: " + config.getResourceLimits()); - - assertNotNull(config.getImageName()); - assertEquals(SandboxType.BASE, config.getSandboxType()); - assertEquals("high", config.getSecurityLevel()); - assertEquals(60, config.getTimeout()); - assertEquals("Custom Python Sandbox with extra security", config.getDescription()); - assertNotNull(config.getResourceLimits()); - assertEquals("2g", config.getResourceLimits().get("memory")); - Object cpuValue = config.getResourceLimits().get("cpu"); - assertTrue(cpuValue != null && - (cpuValue.equals("2.0") || cpuValue.equals(2.0)), - "CPU value should be 2.0"); - }); - } - - @Test - @DisplayName("Test MyCustomSandbox registration (custom type)") - public void testMyCustomSandboxRegistration() { - System.out.println("\n--- Test MyCustomSandbox Registration ---"); - - boolean customRegistered = SandboxRegistryService.isCustomTypeRegistered("custom_sandbox"); - assertTrue(customRegistered, "MyCustomSandbox should be registered"); - System.out.println("✓ MyCustomSandbox registered (custom type: custom_sandbox)"); - - SandboxRegistryService.getCustomTypeConfig("custom_sandbox").ifPresent(config -> { - System.out.println("\nConfiguration:"); - System.out.println(" Image: " + config.getImageName()); - System.out.println(" Security Level: " + config.getSecurityLevel()); - System.out.println(" Timeout: " + config.getTimeout() + "s"); - System.out.println(" Description: " + config.getDescription()); - System.out.println(" Environment: " + config.getEnvironment()); - - assertNotNull(config.getImageName()); - assertTrue(config.getImageName().contains("custom_sandbox")); - assertEquals("medium", config.getSecurityLevel()); - assertEquals(60, config.getTimeout()); - assertEquals("my sandbox", config.getDescription()); - assertNotNull(config.getEnvironment()); - assertTrue(config.getEnvironment().containsKey("TAVILY_API_KEY")); - assertTrue(config.getEnvironment().containsKey("AMAP_MAPS_API_KEY")); - }); - } - - @Test - @DisplayName("Test AdvancedCustomSandbox registration (full configuration)") - public void testAdvancedCustomSandboxRegistration() { - System.out.println("\n--- Test AdvancedCustomSandbox Registration ---"); - - boolean advancedRegistered = SandboxRegistryService.isCustomTypeRegistered("advanced_sandbox"); - assertTrue(advancedRegistered, "AdvancedCustomSandbox should be registered"); - System.out.println("✓ AdvancedCustomSandbox registered (custom type: advanced_sandbox)"); - - SandboxRegistryService.getCustomTypeConfig("advanced_sandbox").ifPresent(config -> { - System.out.println("\nConfiguration:"); - System.out.println(" Image: " + config.getImageName()); - System.out.println(" Security Level: " + config.getSecurityLevel()); - System.out.println(" Timeout: " + config.getTimeout() + "s"); - System.out.println(" Description: " + config.getDescription()); - System.out.println(" Environment: " + config.getEnvironment()); - System.out.println(" Resource Limits: " + config.getResourceLimits()); - System.out.println(" Runtime Config: " + config.getRuntimeConfig()); - - assertEquals("my-registry/my-advanced-sandbox:latest", config.getImageName()); - assertEquals("high", config.getSecurityLevel()); - assertEquals(120, config.getTimeout()); - assertEquals("Advanced custom sandbox with full configuration", config.getDescription()); - - assertNotNull(config.getEnvironment()); - assertEquals(3, config.getEnvironment().size()); - assertTrue(config.getEnvironment().containsKey("API_KEY")); - assertEquals("true", config.getEnvironment().get("DEBUG_MODE")); - assertEquals("4", config.getEnvironment().get("MAX_WORKERS")); - - assertNotNull(config.getResourceLimits()); - assertEquals("4g", config.getResourceLimits().get("memory")); - Object cpuValue = config.getResourceLimits().get("cpu"); - assertTrue(cpuValue != null && - (cpuValue.equals("4.0") || cpuValue.equals(4.0)), - "CPU value should be 4.0"); - - assertNotNull(config.getRuntimeConfig()); - Object enableGpu = config.getRuntimeConfig().get("enable_gpu"); - assertTrue(enableGpu != null && - (enableGpu.equals("true") || enableGpu.equals(true))); - - Object maxConnections = config.getRuntimeConfig().get("max_connections"); - assertTrue(maxConnections != null && - (maxConnections.equals("100") || maxConnections.equals(100))); - - assertTrue(config.getRuntimeConfig().containsKey("nano_cpus")); - assertTrue(config.getRuntimeConfig().containsKey("mem_limit")); - }); - } - - @Test - @DisplayName("Test all custom sandboxes registration status") - public void testAllCustomSandboxesRegistered() { - System.out.println("\n--- Test All Custom Sandboxes Registration Status ---"); - - assertTrue(SandboxRegistryService.isRegistered(SandboxType.BASE), - "CustomPythonSandbox should be registered"); - assertTrue(SandboxRegistryService.isCustomTypeRegistered("custom_sandbox"), - "MyCustomSandbox should be registered"); - assertTrue(SandboxRegistryService.isCustomTypeRegistered("advanced_sandbox"), - "AdvancedCustomSandbox should be registered"); - - System.out.println("✓ All custom sandboxes successfully registered"); - - Map customTypes = - SandboxRegistryService.listAllCustomTypes(); - System.out.println("\nRegistered custom types count: " + customTypes.size()); - customTypes.forEach((name, config) -> - System.out.println(" - " + name + " -> " + config.getImageName()) - ); - } - - @Test - @DisplayName("Test getting custom sandbox image names") - public void testGetImageNames() { - System.out.println("\n--- Test Getting Image Names ---"); - - SandboxRegistryService.getImageByType(SandboxType.BASE).ifPresent(image -> { - System.out.println("CustomPythonSandbox image: " + image); - assertFalse(image.isEmpty()); - assertTrue(image.contains("runtime-sandbox-python")); - }); - - SandboxRegistryService.getCustomTypeImage("custom_sandbox").ifPresent(image -> { - System.out.println("MyCustomSandbox image: " + image); - assertFalse(image.isEmpty()); - }); - - SandboxRegistryService.getCustomTypeImage("advanced_sandbox").ifPresent(image -> { - System.out.println("AdvancedCustomSandbox image: " + image); - assertEquals("my-registry/my-advanced-sandbox:latest", image); - }); - } - - @Test - @DisplayName("Test configuration override") - public void testConfigurationOverride() { - System.out.println("\n--- Test Configuration Override ---"); - - SandboxRegistryService.getConfigByType(SandboxType.BASE).ifPresent(config -> { - System.out.println("\nCustom configuration for BASE type:"); - System.out.println(" Security Level: " + config.getSecurityLevel()); - System.out.println(" Timeout: " + config.getTimeout()); - - assertEquals("high", config.getSecurityLevel(), - "Custom configuration should override default security level"); - assertEquals(60, config.getTimeout(), - "Custom configuration should override default timeout"); - }); - } - - @Test - @DisplayName("Verify annotation configuration completeness") - public void testAnnotationConfigurationCompleteness() { - System.out.println("\n--- Verify Annotation Configuration Completeness ---"); - - SandboxRegistryService.getCustomTypeConfig("advanced_sandbox").ifPresentOrElse( - config -> { - System.out.println("Verifying AdvancedCustomSandbox configuration..."); - - assertNotNull(config.getImageName()); - assertNotNull(config.getSecurityLevel()); - assertTrue(config.getTimeout() > 0); - assertNotNull(config.getDescription()); - assertNotNull(config.getEnvironment()); - assertFalse(config.getEnvironment().isEmpty()); - assertNotNull(config.getResourceLimits()); - assertFalse(config.getResourceLimits().isEmpty()); - assertNotNull(config.getRuntimeConfig()); - assertFalse(config.getRuntimeConfig().isEmpty()); - - System.out.println("✓ All configuration items verified"); - }, - () -> fail("AdvancedCustomSandbox configuration not found") - ); - } -} diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/K8sSandboxLifecycleTest.java b/core/src/test/java/io/agentscope/runtime/sandbox/manager/K8sSandboxLifecycleTest.java deleted file mode 100644 index f233d555..00000000 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/K8sSandboxLifecycleTest.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.agentscope.runtime.sandbox.manager; - -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import org.junit.jupiter.api.condition.EnabledIf; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.AfterEach; - -import static org.junit.jupiter.api.Assertions.*; - -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; - -import java.util.UUID; - -/** - * Kubernetes Sandbox Lifecycle Test - * Tests sandbox creation, startup, status checking, stopping and cleanup functionality in Kubernetes environment - */ -@EnabledIfEnvironmentVariable(named = "KUBECONFIG_PATH", matches = ".+") -@EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") -public class K8sSandboxLifecycleTest { - private static boolean isCI() { - return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); - } - - private SandboxManager sandboxManager; - private String testUserId; - private String testSessionId; - - @BeforeEach - void setUp() { - // Generate test user ID and session ID - testUserId = "test-user-" + UUID.randomUUID().toString().substring(0, 8); - testSessionId = "test-session-" + UUID.randomUUID().toString().substring(0, 8); - - // Initialize Kubernetes sandbox manager - try { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().kubeConfigPath(System.getenv("KUBECONFIG_PATH")).build(); - System.out.println(System.getenv("KUBECONFIG_PATH")); - ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) - .build(); - sandboxManager = new SandboxManager(config); - System.out.println("Kubernetes SandboxManager initialized successfully"); - } catch (Exception e) { - System.err.println("Failed to initialize Kubernetes SandboxManager: " + e.getMessage()); - throw new RuntimeException("Failed to initialize test environment", e); - } - } - - @AfterEach - void tearDown() { - if (sandboxManager != null) { - try { - // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); - System.out.println("All test sandboxes cleaned up successfully"); - } catch (Exception e) { - System.err.println("Error during cleanup: " + e.getMessage()); - } - } - } - - /** - * Test base sandbox creation and startup - */ - @Test - void testCreateAndStartBaseSandbox() { - System.out.println("Testing BASE sandbox creation and startup..."); - - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - - assertNotNull(sandbox, "Sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Sandbox container name should not be null"); - - System.out.println("Created BASE sandbox: " + sandbox.getContainerId()); - - // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); - assertNotNull(status, "Sandbox status should not be null"); - System.out.println("BASE sandbox status: " + status); - } - - /** - * Test browser sandbox creation and startup - */ - @Test - void testCreateAndStartBrowserSandbox() { - System.out.println("Testing BROWSER sandbox creation and startup..."); - - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); - - assertNotNull(sandbox, "Browser sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Browser sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Browser sandbox container name should not be null"); - - System.out.println("Created BROWSER sandbox: " + sandbox.getContainerId()); - - // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.BROWSER, testUserId, testSessionId); - assertNotNull(status, "Browser sandbox status should not be null"); - System.out.println("BROWSER sandbox status: " + status); - } - - /** - * Test filesystem sandbox creation and startup - */ - @Test - void testCreateAndStartFilesystemSandbox() { - System.out.println("Testing FILESYSTEM sandbox creation and startup..."); - - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.FILESYSTEM, testUserId, testSessionId); - - assertNotNull(sandbox, "Filesystem sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Filesystem sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Filesystem sandbox container name should not be null"); - - System.out.println("Created FILESYSTEM sandbox: " + sandbox.getContainerId()); - - // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.FILESYSTEM, testUserId, testSessionId); - assertNotNull(status, "Filesystem sandbox status should not be null"); - System.out.println("FILESYSTEM sandbox status: " + status); - } - - /** - * Test training sandbox creation and startup - */ - @Test - void testCreateAndStartTrainingSandbox() { - System.out.println("Testing TRAINING sandbox creation and startup..."); - - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.TRAINING, testUserId, testSessionId); - - assertNotNull(sandbox, "Training sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Training sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Training sandbox container name should not be null"); - - System.out.println("Created TRAINING sandbox: " + sandbox.getContainerId()); - - // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.TRAINING, testUserId, testSessionId); - assertNotNull(status, "Training sandbox status should not be null"); - System.out.println("TRAINING sandbox status: " + status); - } - - /** - * Test multiple sandboxes running concurrently - */ - @Test - void testMultipleSandboxesConcurrently() { - System.out.println("Testing multiple sandboxes running concurrently..."); - - // Create multiple sandboxes of different types - ContainerModel baseSandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - ContainerModel browserSandbox = sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); - ContainerModel filesystemSandbox = sandboxManager.getSandbox(SandboxType.FILESYSTEM, testUserId, testSessionId); - ContainerModel trainingSandbox = sandboxManager.getSandbox(SandboxType.TRAINING, testUserId, testSessionId); - - // Verify all sandboxes are created successfully - assertNotNull(baseSandbox, "Base sandbox should be created successfully"); - assertNotNull(browserSandbox, "Browser sandbox should be created successfully"); - assertNotNull(filesystemSandbox, "Filesystem sandbox should be created successfully"); - assertNotNull(trainingSandbox, "Training sandbox should be created successfully"); - - // Verify all sandboxes have different container IDs - assertNotEquals(baseSandbox.getContainerId(), browserSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - assertNotEquals(baseSandbox.getContainerId(), filesystemSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - assertNotEquals(browserSandbox.getContainerId(), filesystemSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - assertNotEquals(trainingSandbox.getContainerId(), baseSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - - System.out.println("All sandboxes created successfully:"); - System.out.println("- BASE: " + baseSandbox.getContainerId()); - System.out.println("- BROWSER: " + browserSandbox.getContainerId()); - System.out.println("- FILESYSTEM: " + filesystemSandbox.getContainerId()); - System.out.println("- TRAINING: " + trainingSandbox.getContainerId()); - - // Check all sandbox statuses - String baseStatus = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); - String browserStatus = sandboxManager.getSandboxStatus(SandboxType.BROWSER, testUserId, testSessionId); - String filesystemStatus = sandboxManager.getSandboxStatus(SandboxType.FILESYSTEM, testUserId, testSessionId); - String trainingStatus = sandboxManager.getSandboxStatus(SandboxType.TRAINING, testUserId, testSessionId); - assertNotNull(baseStatus, "Base sandbox status should not be null"); - assertNotNull(browserStatus, "Browser sandbox status should not be null"); - assertNotNull(filesystemStatus, "Filesystem sandbox status should not be null"); - assertNotNull(trainingStatus, "Training sandbox status should not be null"); - } - - /** - * Test sandbox status checking - */ - @Test - void testSandboxStatusCheck() { - System.out.println("Testing sandbox status check..."); - - // Create sandbox - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - assertNotNull(sandbox, "Sandbox should be created successfully"); - - // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); - assertNotNull(status, "Sandbox status should not be null"); - assertNotEquals("not_found", status, "Sandbox status should not be 'not_found'"); - - System.out.println("Sandbox status: " + status); - } - - /** - * Test sandbox stopping and cleanup - */ - @Test - void testStopAndRemoveSandbox() { - System.out.println("Testing sandbox stop and removal..."); - - // Create sandbox - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - assertNotNull(sandbox, "Sandbox should be created successfully"); - - String containerId = sandbox.getContainerId(); - System.out.println("Created sandbox: " + containerId); - - // Stop and remove sandbox - sandboxManager.stopAndRemoveSandbox(SandboxType.BASE, testUserId, testSessionId); - System.out.println("Sandbox stopped and removed"); - - // Verify sandbox has been deleted - String status = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); - assertEquals("not_found", status, "Sandbox status should be 'not_found' indicating it has been deleted"); - - System.out.println("Sandbox successfully removed, status: " + status); - } - - /** - * Test cleanup of all sandboxes - */ - @Test - void testCleanupAllSandboxes() { - System.out.println("Testing cleanup of all sandboxes..."); - - // Create multiple sandboxes - sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); - sandboxManager.getSandbox(SandboxType.FILESYSTEM, testUserId, testSessionId); - - System.out.println("Created multiple sandboxes"); - - // Clean up all sandboxes - sandboxManager.cleanupAllSandboxes(); - System.out.println("All sandboxes cleaned up"); - - // Verify all sandboxes have been deleted - assertEquals("not_found", sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId)); - assertEquals("not_found", sandboxManager.getSandboxStatus(SandboxType.BROWSER, testUserId, testSessionId)); - assertEquals("not_found", sandboxManager.getSandboxStatus(SandboxType.FILESYSTEM, testUserId, testSessionId)); - - System.out.println("All sandboxes successfully cleaned up"); - } - - /** - * Test getting all sandbox information - */ - @Test - void testGetAllSandboxes() { - System.out.println("Testing get all sandboxes..."); - - // Create some sandboxes - sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); - - // Get all sandbox information - var allSandboxes = sandboxManager.getAllSandboxes(); - assertNotNull(allSandboxes, "Sandbox mapping should not be null"); - assertTrue(allSandboxes.size() >= 2, "Should have at least 2 sandboxes"); - - System.out.println("Total sandboxes: " + allSandboxes.size()); - allSandboxes.forEach((key, model) -> System.out.println("Sandbox: " + key + " -> " + model.getContainerId())); - } - - /** - * Test error case: duplicate creation of same sandbox - */ - @Test - void testDuplicateSandboxCreation() { - System.out.println("Testing duplicate sandbox creation..."); - - // Create first sandbox - ContainerModel sandbox1 = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - assertNotNull(sandbox1, "First sandbox should be created successfully"); - - // Try to create sandbox of same type (should return existing sandbox) - ContainerModel sandbox2 = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - assertNotNull(sandbox2, "Should return existing sandbox"); - assertEquals(sandbox1.getContainerId(), sandbox2.getContainerId(), "Duplicate creation should return the same sandbox instance"); - - System.out.println("Duplicate sandbox creation handled correctly"); - } -} diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/RedisSandboxManagerTest.java b/core/src/test/java/io/agentscope/runtime/sandbox/manager/RedisSandboxManagerTest.java deleted file mode 100644 index e6979e84..00000000 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/RedisSandboxManagerTest.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.agentscope.runtime.sandbox.manager; - -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.RedisManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxKey; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.model.container.PortRange; -import io.agentscope.runtime.sandbox.manager.model.fs.LocalFileSystemConfig; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.condition.EnabledIf; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -@EnabledIfDockerAvailable -@Testcontainers -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") -public class RedisSandboxManagerTest { - - private static boolean isCI() { - return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); - } - - private static final String TEST_USER_ID = "test_user"; - private static final String TEST_SESSION_ID = "test_session"; - - @Container - private static final GenericContainer redisContainer = new GenericContainer<>( - DockerImageName.parse("valkey/valkey:8.1.2")) - .withExposedPorts(6379); // #gitleaks:allow - - /** - * Helper method to create RedisManagerConfig using the testcontainer's Redis instance - */ - private static RedisManagerConfig createRedisConfig(String portKey, String poolKey) { - return RedisManagerConfig.builder() - .redisServer(redisContainer.getHost()) - .redisPort(redisContainer.getMappedPort(6379)) - .redisDb(0) - .redisPassword(null) - .redisPortKey(portKey) - .redisContainerPoolKey(poolKey) - .build(); - } - - @Test - @Order(1) - public void testSandboxManagerWithRedis() { - RedisManagerConfig redisConfig = createRedisConfig( - "_test_runtime_sandbox_occupied_ports", - "_test_runtime_sandbox_pool"); - - ManagerConfig config = ManagerConfig.builder() - .containerPrefixKey("redis_test_") - .redisConfig(redisConfig) - .poolSize(2) - .portRange(new PortRange(50000, 51000)) - .fileSystemConfig(LocalFileSystemConfig.builder() - .mountDir("sessions_mount_dir") - .build()) - .build(); - - try (SandboxManager manager = new SandboxManager(config)) { - manager.start(); - assertNotNull(manager, "SandboxManager should be initialized"); - - ContainerModel container = manager.getSandbox( - SandboxType.BASE, - TEST_USER_ID, - TEST_SESSION_ID - ); - - assertNotNull(container, "Container should be created"); - assertNotNull(container.getContainerName(), "Container should have a name"); - assertNotNull(container.getBaseUrl(), "Container should have a base URL"); - assertTrue(container.getBaseUrl().contains("localhost"), "Base URL should contain localhost"); - - Map allSandboxes = manager.getAllSandboxes(); - assertEquals(1, allSandboxes.size(), "Should have 1 sandbox"); - - manager.stopAndRemoveSandbox(SandboxType.BASE, TEST_USER_ID, TEST_SESSION_ID); - - } catch (Exception e) { - fail("Redis test failed: " + e.getMessage(), e); - } - } - - @Test - @Order(2) - public void testSandboxManagerWithoutRedis() { - ManagerConfig config = ManagerConfig.builder() - .containerPrefixKey("inmemory_test_") - .poolSize(0) - .portRange(new PortRange(51000, 52000)) - .fileSystemConfig(LocalFileSystemConfig.builder() - .mountDir("sessions_mount_dir") - .build()) - .build(); - - try (SandboxManager manager = new SandboxManager(config)) { - assertNotNull(manager, "SandboxManager should be initialized"); - - assertFalse(manager.getManagerConfig().getRedisEnabled(), - "Redis should not be enabled"); - - ContainerModel container = manager.getSandbox( - SandboxType.BASE, - "memory_user", - "memory_session" - ); - - assertNotNull(container, "Container should be created"); - assertNotNull(container.getContainerName(), "Container should have a name"); - - manager.stopAndRemoveSandbox(SandboxType.BASE, "memory_user", "memory_session"); - - } catch (Exception e) { - fail("In-memory test failed: " + e.getMessage(), e); - } - } - - @Test - @Order(3) - public void testSharedStateWithRedis() { - RedisManagerConfig redisConfig = createRedisConfig( - "_shared_test_ports", - "_shared_test_pool"); - - ManagerConfig config = ManagerConfig.builder() - .containerPrefixKey("shared_test_") - .redisConfig(redisConfig) - .poolSize(0) - .portRange(new PortRange(52000, 53000)) - .fileSystemConfig(LocalFileSystemConfig.builder() - .mountDir("sessions_mount_dir") - .build()) - .build(); - - ContainerModel container1; - String containerName1; - - try (SandboxManager manager1 = new SandboxManager(config)) { - container1 = manager1.getSandbox( - SandboxType.BASE, - "shared_user", - "shared_session" - ); - - assertNotNull(container1, "Container should be created by manager1"); - containerName1 = container1.getContainerName(); - - try (SandboxManager manager2 = new SandboxManager(config)) { - ContainerModel container2 = manager2.getSandbox( - SandboxType.BASE, - "shared_user", - "shared_session" - ); - - assertNotNull(container2, "Container should be accessible from manager2"); - assertEquals(containerName1, container2.getContainerName(), - "Both managers should access the same container"); - - manager2.stopAndRemoveSandbox(SandboxType.BASE, "shared_user", "shared_session"); - } - - } catch (Exception e) { - fail("Shared state test failed: " + e.getMessage(), e); - } - } - - @Test - @Order(4) - public void testContainerPoolWithRedis() { - RedisManagerConfig redisConfig = createRedisConfig( - "_pool_test_ports", - "_pool_test_queue"); - - ManagerConfig config = ManagerConfig.builder() - .containerPrefixKey("pool_test_") - .redisConfig(redisConfig) - .poolSize(2) - .portRange(new PortRange(53000, 54000)) - .fileSystemConfig(LocalFileSystemConfig.builder() - .mountDir("sessions_mount_dir") - .build()) - .build(); - - try (SandboxManager manager = new SandboxManager(config)) { - assertNotNull(manager, "SandboxManager should be initialized"); - - ContainerModel container = manager.createFromPool( - SandboxType.BASE, - "pool_user", - "pool_session" - ); - - assertNotNull(container, "Container should be created from pool"); - assertNotNull(container.getContainerName(), "Container should have a name"); - - manager.release(container.getContainerName()); - - } catch (Exception e) { - fail("Container pool test failed: " + e.getMessage(), e); - } - } - - @Test - @Order(5) - public void testContainerPersistenceInRedis() { - RedisManagerConfig redisConfig = createRedisConfig( - "_persist_test_ports", - "_persist_test_pool"); - - ManagerConfig config = ManagerConfig.builder() - .containerPrefixKey("persist_test_") - .redisConfig(redisConfig) - .poolSize(0) - .portRange(new PortRange(54000, 55000)) - .fileSystemConfig(LocalFileSystemConfig.builder() - .mountDir("sessions_mount_dir") - .build()) - .build(); - - String containerName; - - try (SandboxManager manager = new SandboxManager(config)) { - ContainerModel container = manager.getSandbox( - SandboxType.BASE, - "persist_user", - "persist_session" - ); - - containerName = container.getContainerName(); - assertNotNull(containerName, "Container should have a name"); - - Map sandboxes = manager.getAllSandboxes(); - assertFalse(sandboxes.isEmpty(), "Should have at least one sandbox in Redis"); - - manager.stopAndRemoveSandbox(SandboxType.BASE, "persist_user", "persist_session"); - } - } - - @AfterEach - public void afterEach() throws InterruptedException { - Thread.sleep(1000); - } -} - diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/SandboxRegistryServiceTest.java b/core/src/test/java/io/agentscope/runtime/sandbox/manager/SandboxRegistryServiceTest.java deleted file mode 100644 index 6b728491..00000000 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/SandboxRegistryServiceTest.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.agentscope.runtime.sandbox.manager; - -import io.agentscope.runtime.sandbox.manager.model.container.DynamicSandboxType; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxConfig; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.registry.SandboxRegistryService; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * SandboxRegistryService and SandboxConfig Test - */ -public class SandboxRegistryServiceTest { - - /** - * Test 1: Using predefined sandbox types - */ - @Test - void testPredefinedSandboxTypes() { - System.out.println("\n--- Test 1: Predefined Sandbox Types ---"); - - // Get predefined type configuration - Optional baseConfig = SandboxRegistryService.getConfigByType(SandboxType.BASE); - assertTrue(baseConfig.isPresent(), "BASE type should exist"); - - baseConfig.ifPresent(config -> { - System.out.println("BASE Sandbox:"); - System.out.println(" Image: " + config.getImageName()); - System.out.println(" Timeout: " + config.getTimeout() + "s"); - System.out.println(" Security Level: " + config.getSecurityLevel()); - - assertNotNull(config.getImageName(), "Image name should not be null"); - assertEquals(30, config.getTimeout(), "Timeout should be greater than 0"); - }); - - // Get image name directly - Optional browserImage = SandboxRegistryService.getImageByType(SandboxType.BROWSER); - assertTrue(browserImage.isPresent(), "BROWSER image should exist"); - - browserImage.ifPresent(image -> { - System.out.println("\nBROWSER Sandbox Image: " + image); - assertFalse(image.isEmpty(), "Image name should not be empty"); - }); - - // Check if type is registered - boolean isRegistered = SandboxRegistryService.isRegistered(SandboxType.FILESYSTEM); - System.out.println("\nFILESYSTEM type registered: " + isRegistered); - assertTrue(isRegistered, "FILESYSTEM type should be registered"); - } - - /** - * Test 2: Register custom sandbox type with basic configuration - */ - @Test - void testRegisterCustomSandboxTypeBasic() { - System.out.println("\n--- Test 2: Custom Sandbox Type (Basic) ---"); - - String customTypeName = "test_custom_sandbox_" + System.currentTimeMillis(); - String customImage = "testcompany/custom-sandbox:latest"; - - // Register custom sandbox type - SandboxRegistryService.registerCustomType(customTypeName, customImage); - - // Retrieve custom type configuration - Optional retrievedImage = SandboxRegistryService.getCustomTypeImage(customTypeName); - assertTrue(retrievedImage.isPresent(), "Custom type should be registered"); - - retrievedImage.ifPresent(image -> { - System.out.println("Custom Sandbox Image: " + image); - assertEquals(customImage, image, "Image name should match"); - }); - - // Check if custom type is registered - boolean isCustomRegistered = SandboxRegistryService.isCustomTypeRegistered(customTypeName); - System.out.println("Custom type registered: " + isCustomRegistered); - assertTrue(isCustomRegistered, "Custom type should be registered"); - } - - /** - * Test 3: Register custom sandbox type with advanced configuration - */ - @Test - void testRegisterCustomSandboxTypeAdvanced() { - System.out.println("\n--- Test 3: Custom Sandbox Type (Advanced) ---"); - - String customTypeName = "test_advanced_sandbox_" + System.currentTimeMillis(); - - // Prepare resource limits - Map resourceLimits = new HashMap<>(); - resourceLimits.put("memory", "2g"); - resourceLimits.put("cpu", 2.0); // 2 CPUs - - // Prepare environment variables - Map environment = new HashMap<>(); - environment.put("CUSTOM_VAR", "custom_value"); - environment.put("DEBUG_MODE", "true"); - - // Prepare runtime configuration - Map runtimeConfig = new HashMap<>(); - runtimeConfig.put("shm_size", "512m"); - - // Register with full configuration - SandboxRegistryService.registerCustomType( - customTypeName, - "testcompany/advanced-sandbox:v1.0", - resourceLimits, - "high", // security level - 600, // timeout in seconds - "Advanced custom sandbox for special tasks", - environment, - runtimeConfig - ); - - // Retrieve and display configuration - Optional config = SandboxRegistryService.getCustomTypeConfig(customTypeName); - assertTrue(config.isPresent(), "Custom configuration should exist"); - - config.ifPresent(cfg -> { - System.out.println("Advanced Sandbox Configuration:"); - System.out.println(" Image: " + cfg.getImageName()); - System.out.println(" Timeout: " + cfg.getTimeout() + "s"); - System.out.println(" Security Level: " + cfg.getSecurityLevel()); - System.out.println(" Description: " + cfg.getDescription()); - System.out.println(" Resource Limits: " + cfg.getResourceLimits()); - System.out.println(" Environment: " + cfg.getEnvironment()); - System.out.println(" Runtime Config: " + cfg.getRuntimeConfig()); - - assertEquals("testcompany/advanced-sandbox:v1.0", cfg.getImageName()); - assertEquals(600, cfg.getTimeout()); - assertEquals("high", cfg.getSecurityLevel()); - assertNotNull(cfg.getResourceLimits()); - assertEquals("2g", cfg.getResourceLimits().get("memory")); - assertEquals(2.0, cfg.getResourceLimits().get("cpu")); - assertNotNull(cfg.getEnvironment()); - assertEquals("custom_value", cfg.getEnvironment().get("CUSTOM_VAR")); - }); - } - - /** - * Test 4: Using DynamicSandboxType for runtime type handling - */ - @Test - void testDynamicSandboxType() { - System.out.println("\n--- Test 4: DynamicSandboxType ---"); - - // Create dynamic sandbox type - String customTypeName = "ml_training_" + System.currentTimeMillis(); - DynamicSandboxType customType = DynamicSandboxType.custom(customTypeName); - - System.out.println("Created dynamic type: " + customType.getTypeName()); - System.out.println("Is custom type: " + customType.isCustom()); - - assertEquals(customTypeName, customType.getTypeName(), "Type name should match"); - assertTrue(customType.isCustom(), "Should be a custom type"); - - // Use predefined type via DynamicSandboxType - DynamicSandboxType baseType = DynamicSandboxType.fromEnum(SandboxType.BASE); - - System.out.println("\nPredefined type: " + baseType.getTypeName()); - System.out.println("Is enum type: " + baseType.isEnum()); - - assertEquals("base", baseType.getTypeName(), "Type name should be base"); - assertTrue(baseType.isEnum(), "Should be an enum type"); - - // Get type by name (works for both predefined and custom types) - try { - DynamicSandboxType retrievedType = DynamicSandboxType.valueOf("BROWSER"); - System.out.println("\nRetrieved type: " + retrievedType.getTypeName()); - assertEquals("browser", retrievedType.getTypeName(), "Retrieved type name should match"); - } catch (IllegalArgumentException e) { - fail("Should be able to find BROWSER type: " + e.getMessage()); - } - } - - /** - * Test 5: List all registrations - */ - @Test - void testListAllRegistrations() { - System.out.println("\n--- Test 5: List All Registrations ---"); - - // List all predefined sandbox types - Map allSandboxes = SandboxRegistryService.listAllSandboxesByType(); - System.out.println("\nPredefined Sandbox Types (" + allSandboxes.size() + "):"); - allSandboxes.forEach((type, config) -> - System.out.println(" " + type + " -> " + config.getImageName()) - ); - - assertFalse(allSandboxes.isEmpty(), "Should have at least some predefined types"); - assertTrue(allSandboxes.containsKey(SandboxType.BASE), "Should contain BASE type"); - - // List all custom sandbox types - Map customTypes = SandboxRegistryService.listAllCustomTypes(); - System.out.println("\nCustom Sandbox Types (" + customTypes.size() + "):"); - customTypes.forEach((name, config) -> - System.out.println(" " + name + " -> " + config.getImageName()) - ); - - // Get total count - int totalCount = SandboxRegistryService.getRegisteredCount() + customTypes.size(); - System.out.println("\nTotal registered sandbox types: " + totalCount); - assertTrue(totalCount > 0, "Should have at least one registered type"); - - // List all custom dynamic types - Map dynamicTypes = DynamicSandboxType.getCustomTypes(); - System.out.println("\nDynamic Custom Types (" + dynamicTypes.size() + "):"); - dynamicTypes.forEach((name, type) -> - System.out.println(" " + name + " -> " + type) - ); - } - - /** - * Test 6: Manual config creation - */ - @Test - void testManualConfigCreation() { - System.out.println("\n--- Test 6: Manual Config Creation ---"); - - // Create sandbox configuration using builder pattern - Map resourceLimits = new HashMap<>(); - resourceLimits.put("memory", "4g"); - resourceLimits.put("cpu", 4.0); - - Map env = new HashMap<>(); - env.put("JAVA_OPTS", "-Xmx3g"); - - SandboxConfig config = new SandboxConfig.Builder() - .sandboxType(SandboxType.BASE) - .imageName("custom/java-sandbox:latest") - .resourceLimits(resourceLimits) - .securityLevel("high") - .timeout(900) - .description("Custom Java development sandbox") - .environment(env) - .build(); - - System.out.println("Created config:"); - System.out.println(" " + config); - - assertNotNull(config, "Configuration should be created successfully"); - assertEquals(SandboxType.BASE, config.getSandboxType()); - assertEquals("custom/java-sandbox:latest", config.getImageName()); - assertEquals("high", config.getSecurityLevel()); - assertEquals(900, config.getTimeout()); - assertEquals("Custom Java development sandbox", config.getDescription()); - assertNotNull(config.getResourceLimits()); - assertEquals("4g", config.getResourceLimits().get("memory")); - assertNotNull(config.getEnvironment()); - assertEquals("-Xmx3g", config.getEnvironment().get("JAVA_OPTS")); - } - - /** - * Test 7: Verify registration idempotency - */ - @Test - void testRegistrationIdempotency() { - System.out.println("\n--- Test 7: Registration Idempotency ---"); - - String typeName = "test_idempotent_" + System.currentTimeMillis(); - String image1 = "testcompany/sandbox:v1"; - String image2 = "testcompany/sandbox:v2"; - - // First registration - SandboxRegistryService.registerCustomType(typeName, image1); - Optional firstImage = SandboxRegistryService.getCustomTypeImage(typeName); - assertTrue(firstImage.isPresent(), "First registration should succeed"); - assertEquals(image1, firstImage.get(), "Should be the first image"); - - // Second registration (overwrite) - SandboxRegistryService.registerCustomType(typeName, image2); - Optional secondImage = SandboxRegistryService.getCustomTypeImage(typeName); - assertTrue(secondImage.isPresent(), "Second registration should succeed"); - - System.out.println("First image: " + image1); - System.out.println("Second image: " + secondImage.get()); - - // Verify if overwritten (specific behavior depends on implementation) - assertNotNull(secondImage.get(), "Second registered image should not be null"); - } -} diff --git a/core/test_storage/test_session_folder/config.json b/core/test_storage/test_session_folder/config.json deleted file mode 100644 index d7004f04..00000000 --- a/core/test_storage/test_session_folder/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Local Test Configuration", - "timestamp": "1765351985344", - "source": "Local File System" -} diff --git a/core/test_storage/test_session_folder/initial_file.txt b/core/test_storage/test_session_folder/initial_file.txt deleted file mode 100644 index 460ac973..00000000 --- a/core/test_storage/test_session_folder/initial_file.txt +++ /dev/null @@ -1,3 +0,0 @@ -This file was initially stored in the local file system -Created at: Wed Dec 10 15:33:05 CST 2025 -Used for testing copy from local storage to container diff --git a/core/pom.xml b/engine-core/pom.xml similarity index 65% rename from core/pom.xml rename to engine-core/pom.xml index 5c79c6ae..20139b38 100644 --- a/core/pom.xml +++ b/engine-core/pom.xml @@ -8,7 +8,7 @@ ../pom.xml - agentscope-runtime-core + agentscope-runtime-engine 1.0.1 jar AgentScope Runtime Core @@ -66,33 +66,6 @@ javax.annotation-api - - com.github.docker-java - docker-java - - - org.glassfish.jersey.core - jersey-client - - - org.glassfish.jersey.connectors - jersey-apache-connector - - - org.glassfish.jersey.core - jersey-common - - - org.glassfish.jersey.inject - jersey-hk2 - - - jakarta.ws.rs - jakarta.ws.rs-api - - - - org.glassfish.jersey.core @@ -115,25 +88,6 @@ jakarta.ws.rs-api - - com.github.docker-java - docker-java-transport-zerodep - - - - - io.kubernetes - client-java-api - - - io.kubernetes - client-java - - - io.kubernetes - client-java-extended - - org.springframework.data @@ -144,12 +98,6 @@ lettuce-core - - - com.aliyun.oss - aliyun-sdk-oss - - com.aliyun.openservices.tablestore @@ -177,16 +125,6 @@ - - com.aliyun - agentrun20250910 - - - - com.aliyun - alibabacloud-agentrun20250910 - - org.testcontainers @@ -207,21 +145,9 @@ - com.aliyun - alibabacloud-fc20230330 - 4.0.26 - - - - com.aliyun - fc20230330 - 4.6.4 - - - - com.aliyun - agentbay-sdk - 0.0.6 + io.agentscope + agentscope-runtime-sandbox-core + ${revision}
diff --git a/core/src/main/java/io/agentscope/runtime/adapters/AgentHandler.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/AgentHandler.java similarity index 98% rename from core/src/main/java/io/agentscope/runtime/adapters/AgentHandler.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/AgentHandler.java index c82d3e02..6f644ba2 100644 --- a/core/src/main/java/io/agentscope/runtime/adapters/AgentHandler.java +++ b/engine-core/src/main/java/io/agentscope/runtime/adapters/AgentHandler.java @@ -16,7 +16,7 @@ package io.agentscope.runtime.adapters; import io.agentscope.runtime.engine.schemas.AgentRequest; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; +import io.agentscope.runtime.sandbox.manager.SandboxService; import reactor.core.publisher.Flux; /** diff --git a/core/src/main/java/io/agentscope/runtime/adapters/MessageAdapter.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/MessageAdapter.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/adapters/MessageAdapter.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/MessageAdapter.java diff --git a/core/src/main/java/io/agentscope/runtime/adapters/StreamAdapter.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/StreamAdapter.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/adapters/StreamAdapter.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/StreamAdapter.java diff --git a/core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandler.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandler.java similarity index 95% rename from core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandler.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandler.java index 59dbc5e9..8173f01c 100644 --- a/core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandler.java +++ b/engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandler.java @@ -22,7 +22,7 @@ import io.agentscope.runtime.engine.services.agent_state.StateService; import io.agentscope.runtime.engine.services.memory.service.MemoryService; import io.agentscope.runtime.engine.services.memory.service.SessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; +import io.agentscope.runtime.sandbox.manager.SandboxService; import reactor.core.publisher.Flux; /** @@ -46,6 +46,7 @@ public abstract class AgentScopeAgentHandler implements AgentHandler { protected final StreamAdapter streamAdapter; protected final MessageAdapter messageAdapter; + protected SandboxService sandboxService; // short-term from runtime, specifically for agentscope protected StateService stateService; @@ -54,8 +55,6 @@ public abstract class AgentScopeAgentHandler implements AgentHandler { // long-term from runtime protected MemoryService memoryService; - protected SandboxService sandboxService; - AgentScopeAgentHandler(StateService stateService, SessionHistoryService sessionHistoryService, MemoryService memoryService, SandboxService sandboxService) { this(); this.stateService = stateService; @@ -73,10 +72,6 @@ protected AgentScopeAgentHandler() { this.messageAdapter = new AgentScopeMessageAdapter(); } - public SandboxService getSandboxService() { - return sandboxService; - } - public void setSessionHistoryService(SessionHistoryService sessionHistoryService) { this.sessionHistoryService = sessionHistoryService; } @@ -89,10 +84,6 @@ public void setMemoryService(MemoryService memoryService) { this.memoryService = memoryService; } - public void setSandboxService(SandboxService sandboxService) { - this.sandboxService = sandboxService; - } - /** * Get the framework type this adapter supports. * @@ -137,9 +128,6 @@ public void start() { if (sessionHistoryService != null) { sessionHistoryService.start(); } - if (sandboxService != null) { - sandboxService.start(); - } } /** @@ -154,9 +142,6 @@ public void stop() { if (sessionHistoryService != null) { sessionHistoryService.stop(); } - if (sandboxService != null) { - sandboxService.stop(); - } } /** @@ -180,5 +165,13 @@ public void stop() { @Override public abstract Flux streamQuery(AgentRequest request, Object messages); + public SandboxService getSandboxService(){ + return sandboxService; + } + + public void setSandboxService(SandboxService sandboxService){ + this.sandboxService = sandboxService; + } + } diff --git a/core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapter.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapter.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapter.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapter.java diff --git a/core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapter.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapter.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapter.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapter.java diff --git a/core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapter.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapter.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapter.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapter.java diff --git a/core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapter.java b/engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapter.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapter.java rename to engine-core/src/main/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapter.java diff --git a/core/src/main/java/io/agentscope/runtime/common/collections/InMemoryMapping.java b/engine-core/src/main/java/io/agentscope/runtime/common/collections/InMemoryMapping.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/common/collections/InMemoryMapping.java rename to engine-core/src/main/java/io/agentscope/runtime/common/collections/InMemoryMapping.java diff --git a/core/src/main/java/io/agentscope/runtime/common/collections/InMemoryQueue.java b/engine-core/src/main/java/io/agentscope/runtime/common/collections/InMemoryQueue.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/common/collections/InMemoryQueue.java rename to engine-core/src/main/java/io/agentscope/runtime/common/collections/InMemoryQueue.java diff --git a/core/src/main/java/io/agentscope/runtime/common/collections/InMemorySetCollection.java b/engine-core/src/main/java/io/agentscope/runtime/common/collections/InMemorySetCollection.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/common/collections/InMemorySetCollection.java rename to engine-core/src/main/java/io/agentscope/runtime/common/collections/InMemorySetCollection.java diff --git a/core/src/main/java/io/agentscope/runtime/common/collections/Mapping.java b/engine-core/src/main/java/io/agentscope/runtime/common/collections/Mapping.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/common/collections/Mapping.java rename to engine-core/src/main/java/io/agentscope/runtime/common/collections/Mapping.java diff --git a/core/src/main/java/io/agentscope/runtime/common/collections/Queue.java b/engine-core/src/main/java/io/agentscope/runtime/common/collections/Queue.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/common/collections/Queue.java rename to engine-core/src/main/java/io/agentscope/runtime/common/collections/Queue.java diff --git a/core/src/main/java/io/agentscope/runtime/common/collections/SetCollection.java b/engine-core/src/main/java/io/agentscope/runtime/common/collections/SetCollection.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/common/collections/SetCollection.java rename to engine-core/src/main/java/io/agentscope/runtime/common/collections/SetCollection.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/DeployManager.java b/engine-core/src/main/java/io/agentscope/runtime/engine/DeployManager.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/DeployManager.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/DeployManager.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/Runner.java b/engine-core/src/main/java/io/agentscope/runtime/engine/Runner.java similarity index 98% rename from core/src/main/java/io/agentscope/runtime/engine/Runner.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/Runner.java index 291339f7..0bf1288f 100644 --- a/core/src/main/java/io/agentscope/runtime/engine/Runner.java +++ b/engine-core/src/main/java/io/agentscope/runtime/engine/Runner.java @@ -25,7 +25,7 @@ import io.agentscope.runtime.engine.schemas.Event; import io.agentscope.runtime.engine.schemas.Message; import io.agentscope.runtime.engine.schemas.RunStatus; -import io.agentscope.runtime.sandbox.manager.SandboxManager; +import io.agentscope.runtime.sandbox.manager.SandboxService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; @@ -274,9 +274,9 @@ public AgentHandler getAgent() { return adapter; } - public SandboxManager getSandboxManager() { - return adapter.getSandboxService().getManagerApi(); + public SandboxService getSandboxService() { + return adapter.getSandboxService(); } - + } diff --git a/core/src/main/java/io/agentscope/runtime/engine/helpers/ResponseBuilder.java b/engine-core/src/main/java/io/agentscope/runtime/engine/helpers/ResponseBuilder.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/helpers/ResponseBuilder.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/helpers/ResponseBuilder.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/AgentRequest.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/AgentRequest.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/AgentRequest.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/AgentRequest.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/AgentResponse.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/AgentResponse.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/AgentResponse.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/AgentResponse.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/AudioContent.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/AudioContent.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/AudioContent.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/AudioContent.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/BaseRequest.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/BaseRequest.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/BaseRequest.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/BaseRequest.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/BaseResponse.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/BaseResponse.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/BaseResponse.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/BaseResponse.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/Content.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Content.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/Content.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Content.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/ContentType.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/ContentType.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/ContentType.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/ContentType.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/DataContent.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/DataContent.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/DataContent.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/DataContent.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/Error.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Error.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/Error.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Error.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/Event.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Event.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/Event.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Event.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCall.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCall.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCall.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCall.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCallOutput.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCallOutput.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCallOutput.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/FunctionCallOutput.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/ImageContent.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/ImageContent.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/ImageContent.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/ImageContent.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/Message.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Message.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/Message.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Message.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/MessageType.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/MessageType.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/MessageType.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/MessageType.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/Role.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Role.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/Role.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Role.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/RunStatus.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/RunStatus.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/RunStatus.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/RunStatus.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/SequenceNumberGenerator.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/SequenceNumberGenerator.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/SequenceNumberGenerator.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/SequenceNumberGenerator.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/Session.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Session.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/Session.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/Session.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/schemas/TextContent.java b/engine-core/src/main/java/io/agentscope/runtime/engine/schemas/TextContent.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/schemas/TextContent.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/schemas/TextContent.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/Service.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/Service.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/Service.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/Service.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/ServiceWithLifecycleManager.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/ServiceWithLifecycleManager.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/ServiceWithLifecycleManager.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/ServiceWithLifecycleManager.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/agent_state/InMemoryStateService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/agent_state/InMemoryStateService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/agent_state/InMemoryStateService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/agent_state/InMemoryStateService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/agent_state/StateService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/agent_state/StateService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/agent_state/StateService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/agent_state/StateService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/InMemoryMemoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/InMemoryMemoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/InMemoryMemoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/InMemoryMemoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/Mem0MemoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/Mem0MemoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/Mem0MemoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/Mem0MemoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/RedisMemoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/RedisMemoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/RedisMemoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/RedisMemoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/SimpleEmbeddingService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/SimpleEmbeddingService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/SimpleEmbeddingService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/SimpleEmbeddingService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/TableStoreMemoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/TableStoreMemoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/TableStoreMemoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/memory/service/TableStoreMemoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/InMemorySessionHistoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/InMemorySessionHistoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/InMemorySessionHistoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/InMemorySessionHistoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/RedisSessionHistoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/RedisSessionHistoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/RedisSessionHistoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/RedisSessionHistoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/TableStoreSessionHistoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/TableStoreSessionHistoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/TableStoreSessionHistoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/persistence/session/TableStoreSessionHistoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/service/EmbeddingService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/service/EmbeddingService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/service/EmbeddingService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/service/EmbeddingService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/service/MemoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/service/MemoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/service/MemoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/service/MemoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/services/memory/service/SessionHistoryService.java b/engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/service/SessionHistoryService.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/services/memory/service/SessionHistoryService.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/services/memory/service/SessionHistoryService.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/shared/Service.java b/engine-core/src/main/java/io/agentscope/runtime/engine/shared/Service.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/shared/Service.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/shared/Service.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/shared/ServiceManager.java b/engine-core/src/main/java/io/agentscope/runtime/engine/shared/ServiceManager.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/shared/ServiceManager.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/shared/ServiceManager.java diff --git a/core/src/main/java/io/agentscope/runtime/engine/shared/ServiceWithLifecycleManager.java b/engine-core/src/main/java/io/agentscope/runtime/engine/shared/ServiceWithLifecycleManager.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/engine/shared/ServiceWithLifecycleManager.java rename to engine-core/src/main/java/io/agentscope/runtime/engine/shared/ServiceWithLifecycleManager.java diff --git a/core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandlerTest.java b/engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandlerTest.java similarity index 99% rename from core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandlerTest.java rename to engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandlerTest.java index d18321a9..ebca8cef 100644 --- a/core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandlerTest.java +++ b/engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeAgentHandlerTest.java @@ -21,7 +21,7 @@ import io.agentscope.runtime.engine.services.agent_state.StateService; import io.agentscope.runtime.engine.services.memory.service.MemoryService; import io.agentscope.runtime.engine.services.memory.service.SessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; +import io.agentscope.runtime.sandbox.manager.SandboxService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; diff --git a/core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapterTest.java b/engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapterTest.java similarity index 100% rename from core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapterTest.java rename to engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeMessageAdapterTest.java diff --git a/core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapterTest.java b/engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapterTest.java similarity index 100% rename from core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapterTest.java rename to engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/AgentScopeStreamAdapterTest.java diff --git a/core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapterTest.java b/engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapterTest.java similarity index 100% rename from core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapterTest.java rename to engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/LongTermMemoryAdapterTest.java diff --git a/core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapterTest.java b/engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapterTest.java similarity index 100% rename from core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapterTest.java rename to engine-core/src/test/java/io/agentscope/runtime/adapters/agentscope/memory/MemoryAdapterTest.java diff --git a/examples/browser_use_fullstack_runtime/backend/pom.xml b/examples/browser_use_fullstack_runtime/backend/pom.xml index 70882ffa..a1cf0586 100755 --- a/examples/browser_use_fullstack_runtime/backend/pom.xml +++ b/examples/browser_use_fullstack_runtime/backend/pom.xml @@ -57,12 +57,12 @@ io.agentscope spring-boot-starter-runtime-a2a - 1.0.0 + 1.0.1 io.agentscope agentscope-runtime-agentscope - 1.0.0 + 1.0.1 diff --git a/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/BrowserAgentApplication.java b/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/BrowserAgentApplication.java index 5313d243..8c8421f0 100755 --- a/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/BrowserAgentApplication.java +++ b/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/BrowserAgentApplication.java @@ -20,11 +20,10 @@ import io.agentscope.runtime.engine.services.agent_state.InMemoryStateService; import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; @@ -71,12 +70,13 @@ public InMemoryMemoryService memoryService() { @Bean public SandboxService sandboxService() { logger.info("Creating SandboxService bean"); - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientConfig(clientConfig) .build(); - SandboxManager sandboxManager = new SandboxManager(managerConfig); - return new SandboxService(sandboxManager); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + return sandboxService; } @Bean diff --git a/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/agent/AgentscopeBrowserUseAgent.java b/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/agent/AgentscopeBrowserUseAgent.java index ae050d55..27610d42 100755 --- a/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/agent/AgentscopeBrowserUseAgent.java +++ b/examples/browser_use_fullstack_runtime/backend/src/main/java/io/agentscope/browser/agent/AgentscopeBrowserUseAgent.java @@ -90,7 +90,7 @@ public synchronized Sandbox connect(String sessionId, String userId) { Sandbox sandbox = null; // Get browser WebSocket URL and VNC info try { - sandbox = this.sandboxService.connect(sessionId, userId, BrowserSandbox.class); + sandbox = new BrowserSandbox(sandboxService, userId, sessionId); // Connect to browser sandbox - use default user/session IDs for initialization ContainerModel sandboxInfo = sandbox.getInfo(); diff --git a/examples/simple_agent_use_examples/agentscope_use_example/pom.xml b/examples/simple_agent_use_examples/agentscope_use_example/pom.xml index 3aaf1a28..ced7c34d 100755 --- a/examples/simple_agent_use_examples/agentscope_use_example/pom.xml +++ b/examples/simple_agent_use_examples/agentscope_use_example/pom.xml @@ -47,6 +47,12 @@ 1.0.1 + + io.agentscope + agentscope-runtime-engine + 1.0.1 + + io.agentscope agentscope-runtime-web diff --git a/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployExample.java b/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployExample.java index f4e19630..d9640cb2 100755 --- a/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployExample.java +++ b/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployExample.java @@ -20,11 +20,10 @@ import io.agentscope.runtime.engine.services.agent_state.InMemoryStateService; import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.jetbrains.annotations.NotNull; /** @@ -46,7 +45,7 @@ private static void runAgent() { agentHandler.setStateService(new InMemoryStateService()); agentHandler.setSessionHistoryService(new InMemorySessionHistoryService()); agentHandler.setMemoryService(new InMemoryMemoryService()); - agentHandler.setSandboxService(buidSandboxService()); + agentHandler.setSandboxService(buildSandboxService()); AgentApp agentApp = new AgentApp(agentHandler); agentApp.cors(registry -> registry.addMapping("/**") @@ -57,14 +56,16 @@ private static void runAgent() { } @NotNull - private static SandboxService buidSandboxService() { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); + private static SandboxService buildSandboxService() { + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - return new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService( + managerConfig + ); + sandboxService.start(); + return sandboxService; } } diff --git a/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployWithCommandLineExample.java b/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployWithCommandLineExample.java index 32270d58..484c3af1 100644 --- a/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployWithCommandLineExample.java +++ b/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/AgentScopeDeployWithCommandLineExample.java @@ -15,49 +15,49 @@ */ package io.agentscope; -import java.util.Objects; -import java.util.Properties; - import io.agentscope.runtime.adapters.AgentHandler; import io.agentscope.runtime.app.AgentApp; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; + +import java.util.Objects; +import java.util.Properties; public class AgentScopeDeployWithCommandLineExample { - public static void main(String[] args) { - String[] commandLine = new String[2]; - commandLine[0] = "-f"; - commandLine[1] = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(".env")).getPath(); - AgentApp app = new AgentApp(commandLine); - app.run(); - } + public static void main(String[] args) { + String[] commandLine = new String[2]; + commandLine[0] = "-f"; + commandLine[1] = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(".env")).getPath(); + AgentApp app = new AgentApp(commandLine); + app.run(); + } - public static class MyAgentHandlerProvider implements AgentApp.AgentHandlerProvider{ + public static class MyAgentHandlerProvider implements AgentApp.AgentHandlerProvider { - @Override - public AgentHandler get(Properties properties, AgentApp.ServiceComponentManager serviceComponentManager) { - MyAgentScopeAgentHandler handler = new MyAgentScopeAgentHandler(); - handler.setStateService(serviceComponentManager.getStateService()); - handler.setSandboxService(serviceComponentManager.getSandboxService()); - handler.setMemoryService(serviceComponentManager.getMemoryService()); - handler.setSessionHistoryService(serviceComponentManager.getSessionHistoryService()); - return handler; - } - } + @Override + public AgentHandler get(Properties properties, AgentApp.ServiceComponentManager serviceComponentManager) { + MyAgentScopeAgentHandler handler = new MyAgentScopeAgentHandler(); + handler.setStateService(serviceComponentManager.getStateService()); + handler.setSandboxService(serviceComponentManager.getSandboxService()); + handler.setMemoryService(serviceComponentManager.getMemoryService()); + handler.setSessionHistoryService(serviceComponentManager.getSessionHistoryService()); + return handler; + } + } - public static class MySandboxServiceProvider implements AgentApp.SandboxServiceProvider { - @Override - public SandboxService get(Properties properties) { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); - ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) - .build(); - return new SandboxService( - new SandboxManager(managerConfig)); - } - } + public static class MySandboxServiceProvider implements AgentApp.SandboxServiceProvider { + @Override + public SandboxService get(Properties properties) { + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) + .build(); + return new SandboxService( + managerConfig + ); + } + } } diff --git a/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/MyAgentScopeAgentHandler.java b/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/MyAgentScopeAgentHandler.java index aa1b34ca..8c32f501 100755 --- a/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/MyAgentScopeAgentHandler.java +++ b/examples/simple_agent_use_examples/agentscope_use_example/src/main/java/io/agentscope/MyAgentScopeAgentHandler.java @@ -33,6 +33,7 @@ import io.agentscope.runtime.engine.schemas.AgentRequest; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.SandboxService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; @@ -50,6 +51,7 @@ * */ public class MyAgentScopeAgentHandler extends AgentScopeAgentHandler { + private SandboxService sandboxService; private static final Logger logger = LoggerFactory.getLogger(MyAgentScopeAgentHandler.class); private final String apiKey; @@ -60,6 +62,14 @@ public MyAgentScopeAgentHandler() { apiKey = System.getenv("AI_DASHSCOPE_API_KEY"); } + public SandboxService getSandboxService() { + return sandboxService; + } + + public void setSandboxService(SandboxService sandboxService) { + this.sandboxService = sandboxService; + } + @Override public Flux streamQuery(AgentRequest request, Object messages) { String sessionId = request.getSessionId(); @@ -85,7 +95,11 @@ public Flux streamQuery(AgentRequest request, Ob if (sandboxService != null) { try { // Create a BaseSandbox instance for this session - Sandbox sandbox = sandboxService.connect(userId, sessionId, BaseSandbox.class); + Sandbox sandbox = new BaseSandbox( + sandboxService, + userId, + sessionId + ); // Register Python code execution tool (matching Python: execute_python_code) toolkit.registerTool(ToolkitInit.RunPythonCodeTool(sandbox)); diff --git a/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/CustomSandbox.java b/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/CustomSandbox.java index 3c336703..985fde12 100644 --- a/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/CustomSandbox.java +++ b/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/CustomSandbox.java @@ -16,29 +16,23 @@ package io.agentscope; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; @RegisterSandbox( imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-browser:latest", - sandboxType = SandboxType.CUSTOM, + sandboxType = "custom", securityLevel = "medium", timeout = 30, description = "Base Sandbox" ) public class CustomSandbox extends Sandbox { - public CustomSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); - } - public CustomSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, - String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.CUSTOM, timeout); + String sessionId) { + super(managerApi, userId, sessionId, "custom"); } } diff --git a/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/Main.java b/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/Main.java index fb2c8fef..d7f841ff 100644 --- a/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/Main.java +++ b/examples/simple_agent_use_examples/custom_sandbox_example/src/main/java/io/agentscope/Main.java @@ -17,25 +17,22 @@ package io.agentscope; import com.google.gson.Gson; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; public class Main { public static void main(String[] args) { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientConfig(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); sandboxService.start(); - try (Sandbox sandbox = sandboxService.connect("sessionId", "userId", CustomSandbox.class)) { + try (Sandbox sandbox = new CustomSandbox(sandboxService, "user1", "session1")) { Gson gson = new Gson(); String tools = gson.toJson(sandbox.listTools("")); System.out.println("Available tools: "); diff --git a/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/config/AgentConfig.java b/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/config/AgentConfig.java index 5875b880..e04169ad 100644 --- a/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/config/AgentConfig.java +++ b/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/config/AgentConfig.java @@ -23,15 +23,12 @@ import io.agentscope.core.model.DashScopeChatModel; import io.agentscope.core.tool.Toolkit; import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BrowserSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import org.checkerframework.checker.units.qual.A; -import org.springframework.beans.factory.annotation.Autowired; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -71,14 +68,12 @@ public DashScopeChatModel chatModel() { */ @Bean public SandboxService sandboxService() { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientConfig(clientConfig) .build(); - SandboxService service = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService service = new SandboxService(managerConfig); service.start(); return service; } @@ -95,12 +90,10 @@ public Toolkit createToolkit(SandboxService sandboxService) { toolkit.registerTool(weatherTool); toolkit.registerTool(calculatorTool); try { - Sandbox sandbox = sandboxService.connect("userId", "sessionId", BrowserSandbox.class); - toolkit.registerTool(ToolkitInit.BrowserNavigateTool(sandbox)); - if (sandbox instanceof BrowserSandbox browserSandbox) { - String desktopUrl = browserSandbox.getDesktopUrl(); - System.out.println("GUI Desktop URL: " + desktopUrl); - } + BrowserSandbox browserSandbox = new BrowserSandbox(sandboxService, "agent-user", "agent-session"); + toolkit.registerTool(ToolkitInit.BrowserNavigateTool(browserSandbox)); + String desktopUrl = browserSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); } catch (Exception ignored) { } return toolkit; diff --git a/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ChatService.java b/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ChatService.java index 15d1e3ea..050a77bb 100644 --- a/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ChatService.java +++ b/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ChatService.java @@ -19,21 +19,11 @@ import com.example.agentscope.model.ChatRequest; import com.example.agentscope.model.ChatResponse; import com.example.agentscope.model.ToolInfo; -import com.example.agentscope.tools.CalculatorTool; -import com.example.agentscope.tools.WeatherTool; import io.agentscope.core.ReActAgent; -import io.agentscope.core.memory.InMemoryMemory; import io.agentscope.core.message.Msg; import io.agentscope.core.message.MsgRole; import io.agentscope.core.message.TextBlock; -import io.agentscope.core.model.DashScopeChatModel; -import io.agentscope.core.model.ToolSchema; import io.agentscope.core.tool.Toolkit; -import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.box.BrowserSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import org.checkerframework.checker.units.qual.A; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ResourceService.java b/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ResourceService.java index a59d8e8e..4fa54e72 100644 --- a/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ResourceService.java +++ b/examples/simple_sandbox_tool_example/src/main/java/com/example/agentscope/service/ResourceService.java @@ -16,7 +16,7 @@ package com.example.agentscope.service; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; +import io.agentscope.runtime.sandbox.manager.SandboxService; import jakarta.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -29,6 +29,6 @@ public class ResourceService { @PreDestroy public void cleanup() { System.out.println("CloseOperation: Releasing resources..."); - sandboxService.stop(); + sandboxService.close(); } } diff --git a/examples/structured_sandbox_tool_example/src/main/java/com/example/agentscope/SandboxStructuredExample.java b/examples/structured_sandbox_tool_example/src/main/java/com/example/agentscope/SandboxStructuredExample.java index 31ec3be3..135c43a4 100644 --- a/examples/structured_sandbox_tool_example/src/main/java/com/example/agentscope/SandboxStructuredExample.java +++ b/examples/structured_sandbox_tool_example/src/main/java/com/example/agentscope/SandboxStructuredExample.java @@ -24,13 +24,11 @@ import io.agentscope.core.model.DashScopeChatModel; import io.agentscope.core.tool.Toolkit; import io.agentscope.runtime.engine.agents.agentscope.tools.ToolkitInit; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.sandbox.box.BrowserSandbox; -import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import java.util.List; @@ -41,24 +39,20 @@ public class SandboxStructuredExample { public static void main(String[] args) throws Exception { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientConfig(clientConfig) .build(); - SandboxService service = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService service = new SandboxService(managerConfig); service.start(); Toolkit toolkit = new Toolkit(); try { - Sandbox sandbox = service.connect("userId", "sessionId", BrowserSandbox.class); - toolkit.registerTool(ToolkitInit.BrowserNavigateTool(sandbox)); - if (sandbox instanceof BrowserSandbox browserSandbox) { - String desktopUrl = browserSandbox.getDesktopUrl(); - System.out.println("GUI Desktop URL: " + desktopUrl); - } + BrowserSandbox browserSandbox = new BrowserSandbox(service, "agent-user", "agent-sessopn"); + toolkit.registerTool(ToolkitInit.BrowserNavigateTool(browserSandbox)); + String desktopUrl = browserSandbox.getDesktopUrl(); + System.out.println("GUI Desktop URL: " + desktopUrl); } catch (Exception ignored) { } @@ -84,7 +78,7 @@ public static void main(String[] args) throws Exception { runExample(agent); System.out.println("\n=== All examples completed ==="); - service.stop(); + service.close(); } /** diff --git a/maven_plugin/src/main/java/io/agentscope/maven/plugin/service/K8sDeployService.java b/maven_plugin/src/main/java/io/agentscope/maven/plugin/service/K8sDeployService.java index 71925621..58a53697 100644 --- a/maven_plugin/src/main/java/io/agentscope/maven/plugin/service/K8sDeployService.java +++ b/maven_plugin/src/main/java/io/agentscope/maven/plugin/service/K8sDeployService.java @@ -50,7 +50,7 @@ public K8sDeployService(Log log) { } /** - * Deploy to Kubernetes following SandboxManager's Kubernetes client logic + * Deploy to Kubernetes following SandboxService's Kubernetes client logic */ public String deploy(String imageName, BuildConfig buildConfig, K8sConfig k8sConfig) throws Exception { ensureConnected(k8sConfig); diff --git a/pom.xml b/pom.xml index 8dfc5e24..fd79ee9b 100644 --- a/pom.xml +++ b/pom.xml @@ -54,13 +54,18 @@ - core + engine-core agents web maven_plugin starters/spring-boot-starter-runtime-a2a examples/simple_agent_use_rocketmq_example/agentscope_use_rocketmq_server_example examples/simple_agent_use_rocketmq_example/agentscope_use_rocketmq_client_example + sandbox-core + sandbox-extensions/agentrun-extension + sandbox-extensions/fc-extension + sandbox-extensions/kubernetes-extension + sandbox-extensions/redis-extension git://github.com/agentscope-ai/agentscope-runtime-java.git @@ -118,6 +123,8 @@ 4.7.7 + + 2.0.17 diff --git a/sandbox-core/pom.xml b/sandbox-core/pom.xml new file mode 100644 index 00000000..5d2ea230 --- /dev/null +++ b/sandbox-core/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + io.agentscope + agentscope-runtime + ${revision} + ../pom.xml + + + agentscope-runtime-sandbox-core + + + 3.7.0 + 3.18.4 + 0.0.6 + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + com.github.docker-java + docker-java-core + ${docker-java.version} + + + + com.github.docker-java + docker-java-transport-zerodep + ${docker-java.version} + + + + com.aliyun.oss + aliyun-sdk-oss + ${oss.version} + + + + com.fasterxml.jackson.core + jackson-core + 2.20.1 + + + + com.aliyun + agentbay-sdk + ${agentbay-sdk.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.testcontainers + junit-jupiter + test + + + + diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/APPWorldSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/APPWorldSandbox.java new file mode 100644 index 00000000..6d6e5fa4 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/APPWorldSandbox.java @@ -0,0 +1,68 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.agentscope.runtime.sandbox.box; + +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; +import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; + +import java.util.Map; + +@RegisterSandbox( + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-appworld:latest", + sandboxType = "appworld", + runtimeConfig = {"shm_size=5.06gb"}, + securityLevel = "medium", + timeout = 30, + description = "appworld Sandbox" +) +public class APPWorldSandbox extends TrainingSandbox { + + public APPWorldSandbox( + SandboxService managerApi, + String userId, + String sessionId) { + this(managerApi, userId, sessionId, Map.of()); + } + + public APPWorldSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, fileSystemConfig, Map.of()); + } + + public APPWorldSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment) { + this(managerApi, userId, sessionId, LocalFileSystemConfig.builder().build(), environment); + } + + public APPWorldSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig, + Map environment + ) { + super(managerApi, userId, sessionId, "appworld", fileSystemConfig, environment); + } +} + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/AgentBaySandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/AgentBaySandbox.java similarity index 85% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/AgentBaySandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/AgentBaySandbox.java index 1ce479dd..2e06202c 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/AgentBaySandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/AgentBaySandbox.java @@ -24,10 +24,8 @@ import com.microsoft.playwright.Browser; import com.microsoft.playwright.Page; import com.microsoft.playwright.Playwright; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.AgentBayClient; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.agentbay.AgentBayClient; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +34,7 @@ @RegisterSandbox( imageName = "agentbay-cloud", - sandboxType = SandboxType.AGENTBAY, + sandboxType = "agentbay", securityLevel = "high", timeout = 300, description = "AgentBay Cloud Sandbox Environment" @@ -44,37 +42,41 @@ public class AgentBaySandbox extends CloudSandbox { private static final Logger logger = LoggerFactory.getLogger(AgentBaySandbox.class); - private String imageId; - private Map labels; + private final String imageId; + private final Map labels; - public AgentBaySandbox(SandboxManager managerApi, String userId, String sessionId) { + public Map getLabels() { + return labels; + } + + public String getImageId() { + return imageId; + } + + public AgentBaySandbox(SandboxService managerApi, String userId, String sessionId) { this(managerApi, userId, sessionId, "linux_latest"); } - public AgentBaySandbox(SandboxManager managerApi, String userId, String sessionId, String imageId) { - this(managerApi, userId, sessionId, 3000, imageId, null); + public AgentBaySandbox(SandboxService managerApi, String userId, String sessionId, String imageId) { + this(managerApi, userId, sessionId, imageId, Map.of()); } - public AgentBaySandbox(SandboxManager managerApi, String userId, String sessionId, - int timeout, String imageId, Map labels) { - super(managerApi, userId, sessionId, timeout, SandboxType.AGENTBAY); - if(imageId == null || imageId.isEmpty()){ + public AgentBaySandbox(SandboxService managerApi, String userId, String sessionId, + String imageId, Map labels) { + super(managerApi, userId, sessionId, "agentbay"); + this.labels = labels; + if (imageId == null || imageId.isEmpty()) { this.imageId = "linux_latest"; } else { this.imageId = imageId; } try { - ContainerModel containerModel = managerApi.createFromPool(sandboxType, userId, sessionId, imageId, labels); - if (containerModel == null) { - throw new RuntimeException( - "No sandbox available. Please check if sandbox images exist." - ); + this.sandboxId = managerApi.createAgentBayContainer(this); + if (this.sandboxId == null) { + logger.error("Failed to initialize sandbox"); } - this.sandboxId = containerModel.getContainerId(); - logger.info("Sandbox initialized: {} (type={}, user={}, session={}, autoRelease={})", this.sandboxId, sandboxType, userId, sessionId, autoRelease); } catch (Exception e) { logger.error("Failed to initialize sandbox: {}", e.getMessage()); - throw new RuntimeException("Failed to initialize sandbox", e); } } @@ -86,10 +88,6 @@ public Map getSessionInfo() { return agentBay.getSessionInfo(this.sandboxId); } - public String getCloudProviderName() { - return "AgentBay"; - } - public Map listTools() { return listTools(null); } @@ -128,38 +126,38 @@ public String callCloudTool(String toolName, Map parameters) { logger.error("AgentBay session not found: {}", this.sandboxId); return "AgentBay session not found: " + this.sandboxId; } - if(Objects.equals(toolName, "run_shell_command")){ + if (Objects.equals(toolName, "run_shell_command")) { String command = (String) parameters.getOrDefault("command", ""); return runShellCommand(command); - } else if(Objects.equals(toolName, "run_ipython_cell")){ + } else if (Objects.equals(toolName, "run_ipython_cell")) { String code = (String) parameters.getOrDefault("code", ""); return runIpythonCell(code); - } else if(Objects.equals(toolName, "read_file")){ + } else if (Objects.equals(toolName, "read_file")) { String path = (String) parameters.getOrDefault("path", ""); return readFile(path); - } else if(Objects.equals(toolName, "write_file")){ + } else if (Objects.equals(toolName, "write_file")) { String path = (String) parameters.getOrDefault("path", ""); String content = (String) parameters.getOrDefault("content", ""); return writeFile(path, content); - } else if(Objects.equals(toolName, "list_directory")){ + } else if (Objects.equals(toolName, "list_directory")) { String path = (String) parameters.getOrDefault("path", ""); return listDirectory(path); - } else if(Objects.equals(toolName, "create_directory")){ + } else if (Objects.equals(toolName, "create_directory")) { String path = (String) parameters.getOrDefault("path", ""); return createDirectory(path); - } else if(Objects.equals(toolName, "move_file")){ + } else if (Objects.equals(toolName, "move_file")) { String source = (String) parameters.getOrDefault("source", ""); String destination = (String) parameters.getOrDefault("destination", ""); return moveFile(source, destination); - } else if(Objects.equals(toolName, "delete_file")){ + } else if (Objects.equals(toolName, "delete_file")) { String path = (String) parameters.getOrDefault("path", ""); return deleteFile(path); - } else if(Objects.equals(toolName, "screenshot")){ + } else if (Objects.equals(toolName, "screenshot")) { return takeScreenShot(); - } else if(Objects.equals(toolName, "browser_navigate")){ + } else if (Objects.equals(toolName, "browser_navigate")) { String url = (String) parameters.getOrDefault("url", ""); return browserNavigate(url); - } else if(Objects.equals(toolName, "browser_click")){ + } else if (Objects.equals(toolName, "browser_click")) { String selector = (String) parameters.getOrDefault("selector", ""); return browserClick(selector); } else { @@ -330,22 +328,18 @@ public String browserNavigate(String url) { logger.error("AgentBay session not found: {}", this.sandboxId); return "AgentBay session not found: " + this.sandboxId; } - Map response = new HashMap<>(); + Map response; String result = ""; try { String endpointUrl = session.getBrowser().getEndpointUrl(); try (Playwright playwright = Playwright.create()) { Browser browser = playwright.chromium().connectOverCDP(endpointUrl); - Page page = browser.newPage(); - // Navigate to a website page.navigate(url); - // Wait for page load page.waitForTimeout(2000); - result = page.content(); browser.close(); } catch (Exception e) { @@ -373,8 +367,8 @@ public String browserClick(String selector) { logger.error("AgentBay session not found: {}", this.sandboxId); return "AgentBay session not found: " + this.sandboxId; } - Map response = new HashMap<>(); - ActResult result = null; + Map response; + ActResult result; try { session.getBrowser().initialize(new BrowserOption()); result = session.getBrowser().getAgent().click(null, selector); diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/BFCLSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BFCLSandbox.java similarity index 55% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/BFCLSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BFCLSandbox.java index 455061df..03559920 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/BFCLSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BFCLSandbox.java @@ -15,13 +15,16 @@ */ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; +import java.util.Map; + @RegisterSandbox( imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-bfcl:latest", - sandboxType = SandboxType.BFCL, + sandboxType = "bfcl", runtimeConfig = {"shm_size=8.06gb"}, securityLevel = "medium", timeout = 30, @@ -33,17 +36,37 @@ } ) public class BFCLSandbox extends TrainingSandbox { - - public BFCLSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); + + public BFCLSandbox( + SandboxService managerApi, + String userId, + String sessionId) { + this(managerApi, userId, sessionId, Map.of()); + } + + public BFCLSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, fileSystemConfig, Map.of()); + } + + public BFCLSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment) { + this(managerApi, userId, sessionId, LocalFileSystemConfig.builder().build(), environment); } public BFCLSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.BFCL, timeout); + FileSystemConfig fileSystemConfig, + Map environment) { + super(managerApi, userId, sessionId, "bfcl", fileSystemConfig, environment); } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/BaseSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BaseSandbox.java similarity index 58% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/BaseSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BaseSandbox.java index 25a36df8..b8113d3f 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/BaseSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BaseSandbox.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; import java.util.HashMap; @@ -25,23 +27,47 @@ @RegisterSandbox( imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-base:latest", - sandboxType = SandboxType.BASE, + sandboxType = "base", securityLevel = "medium", timeout = 30, description = "Base Sandbox" ) public class BaseSandbox extends Sandbox { - public BaseSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); + public BaseSandbox( + SandboxService managerApi, + String userId, + String sessionId + ) { + this(managerApi, userId, sessionId, Map.of()); + } + + public BaseSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig + ) { + this(managerApi, userId, sessionId, fileSystemConfig, Map.of()); + } + + public BaseSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment + ) { + this(managerApi, userId, sessionId, LocalFileSystemConfig.builder().build(), environment); } public BaseSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.BASE, timeout); + FileSystemConfig fileSystemConfig, + Map environment + ) { + super(managerApi, userId, sessionId, "base", fileSystemConfig, environment); } /** @@ -62,3 +88,4 @@ public String runShellCommand(String command) { return callTool("run_shell_command", arguments); } } + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/BrowserSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BrowserSandbox.java similarity index 81% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/BrowserSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BrowserSandbox.java index 488fafdc..99d91e5c 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/BrowserSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/BrowserSandbox.java @@ -15,46 +15,64 @@ */ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; import java.util.HashMap; import java.util.Map; @RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-browser:latest", - sandboxType = SandboxType.BROWSER, - securityLevel = "medium", - timeout = 60, - description = "Browser sandbox" + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-browser:latest", + sandboxType = "browser", + securityLevel = "medium", + timeout = 60, + description = "Browser sandbox" ) public class BrowserSandbox extends Sandbox { - - private String baseUrl; - - public BrowserSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000, null); + + private final String baseUrl; + + public BrowserSandbox(SandboxService managerApi, String userId, String sessionId) { + this(managerApi, userId, sessionId, null, LocalFileSystemConfig.builder().build(), Map.of()); } - + public BrowserSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout) { - this(managerApi, userId, sessionId, timeout, null); + String baseUrl) { + this(managerApi, userId, sessionId, baseUrl, LocalFileSystemConfig.builder().build(), Map.of()); } - + public BrowserSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout, - String baseUrl) { - super(managerApi, userId, sessionId, SandboxType.BROWSER, timeout); + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, null, fileSystemConfig, Map.of()); + } + + public BrowserSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment) { + this(managerApi, userId, sessionId, null, LocalFileSystemConfig.builder().build(), environment); + } + + public BrowserSandbox( + SandboxService managerApi, + String userId, + String sessionId, + String baseUrl, + FileSystemConfig fileSystemConfig, + Map environment) { + super(managerApi, userId, sessionId, "browser", fileSystemConfig, environment); this.baseUrl = baseUrl; } - + /** * Get the desktop URL for VNC access. * This method provides GUI mixin functionality. @@ -65,24 +83,24 @@ public BrowserSandbox( public String getDesktopUrl() { return GuiMixin.getDesktopUrl(managerApi, sandboxId, baseUrl); } - + public String navigate(String url) { Map arguments = new HashMap<>(); arguments.put("url", url); return callTool("browser_navigate", arguments); } - + public String click(String element, String ref) { Map arguments = new HashMap<>(); arguments.put("element", element); arguments.put("ref", ref); return callTool("browser_click", arguments); } - + public String type(String element, String ref, String text) { return type(element, ref, text, null, null); } - + public String type(String element, String ref, String text, Boolean submit, Boolean slowly) { Map arguments = new HashMap<>(); arguments.put("element", element); @@ -92,11 +110,11 @@ public String type(String element, String ref, String text, Boolean submit, Bool if (slowly != null) arguments.put("slowly", slowly); return callTool("browser_type", arguments); } - + public String takeScreenshot() { return takeScreenshot(null, null, null, null); } - + public String takeScreenshot(Boolean raw, String filename, String element, String ref) { Map arguments = new HashMap<>(); if (raw != null) arguments.put("raw", raw); @@ -105,37 +123,37 @@ public String takeScreenshot(Boolean raw, String filename, String element, Strin if (ref != null) arguments.put("ref", ref); return callTool("browser_take_screenshot", arguments); } - + public String snapshot() { return callTool("browser_snapshot", new HashMap<>()); } - + public String tabNew(String url) { Map arguments = new HashMap<>(); if (url != null) arguments.put("url", url); return callTool("browser_tab_new", arguments); } - + public String tabSelect(Integer index) { Map arguments = new HashMap<>(); if (index != null) arguments.put("index", index); return callTool("browser_tab_select", arguments); } - + public String tabClose(Integer index) { Map arguments = new HashMap<>(); if (index != null) arguments.put("index", index); return callTool("browser_tab_close", arguments); } - + public String tabList() { return callTool("browser_tab_list", new HashMap<>()); } - + public String waitFor(Double time) { return waitFor(time, null, null); } - + public String waitFor(Double time, String text, String textGone) { Map arguments = new HashMap<>(); if (time != null) arguments.put("time", time); @@ -143,47 +161,47 @@ public String waitFor(Double time, String text, String textGone) { if (textGone != null) arguments.put("textGone", textGone); return callTool("browser_wait_for", arguments); } - + public String resize(Double width, Double height) { Map arguments = new HashMap<>(); arguments.put("width", width); arguments.put("height", height); return callTool("browser_resize", arguments); } - + public String closeBrowser() { return callTool("browser_close", new HashMap<>()); } - + public String consoleMessages() { return callTool("browser_console_messages", new HashMap<>()); } - + public String networkRequests() { return callTool("browser_network_requests", new HashMap<>()); } - + public String navigateBack() { return callTool("browser_navigate_back", new HashMap<>()); } - + public String navigateForward() { return callTool("browser_navigate_forward", new HashMap<>()); } - + public String hover(String element, String ref) { Map arguments = new HashMap<>(); arguments.put("element", element); arguments.put("ref", ref); return callTool("browser_hover", arguments); } - + public String pressKey(String key) { Map arguments = new HashMap<>(); arguments.put("key", key); return callTool("browser_press_key", arguments); } - + public String handleDialog(Boolean accept, String promptText) { Map arguments = new HashMap<>(); arguments.put("accept", accept); @@ -192,13 +210,13 @@ public String handleDialog(Boolean accept, String promptText) { } return callTool("browser_handle_dialog", arguments); } - + public String fileUpload(String[] paths) { Map arguments = new HashMap<>(); arguments.put("paths", paths); return callTool("browser_file_upload", arguments); } - + public String pdfSave(String filename) { Map arguments = new HashMap<>(); if (filename != null) { @@ -206,7 +224,7 @@ public String pdfSave(String filename) { } return callTool("browser_pdf_save", arguments); } - + public String drag(String startElement, String startRef, String endElement, String endRef) { Map arguments = new HashMap<>(); arguments.put("startElement", startElement); @@ -215,7 +233,7 @@ public String drag(String startElement, String startRef, String endElement, Stri arguments.put("endRef", endRef); return callTool("browser_drag", arguments); } - + public String selectOption(String element, String ref, String[] values) { Map arguments = new HashMap<>(); arguments.put("element", element); diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/CloudSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/CloudSandbox.java similarity index 62% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/CloudSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/CloudSandbox.java index 3f8d1b65..e977ad66 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/CloudSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/CloudSandbox.java @@ -16,20 +16,14 @@ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; public abstract class CloudSandbox extends Sandbox { - public CloudSandbox(SandboxManager managerApi, String userId, String sessionId, SandboxType sandboxType) { - this(managerApi, userId, sessionId, 3000, sandboxType); - } - public CloudSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout, - SandboxType sandboxType) { - super(managerApi, userId, sessionId, sandboxType, timeout); + String sandboxType) { + super(managerApi, userId, sessionId, sandboxType); } } \ No newline at end of file diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/FilesystemSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/FilesystemSandbox.java similarity index 72% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/FilesystemSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/FilesystemSandbox.java index 5d9ac6e7..335f23d6 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/FilesystemSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/FilesystemSandbox.java @@ -15,8 +15,9 @@ */ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; import java.util.HashMap; @@ -24,38 +25,55 @@ import java.util.Map; @RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-filesystem:latest", - sandboxType = SandboxType.FILESYSTEM, - securityLevel = "medium", - timeout = 60, - description = "Filesystem sandbox" + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-filesystem:latest", + sandboxType = "filesystem", + securityLevel = "medium", + timeout = 60, + description = "Filesystem sandbox" ) public class FilesystemSandbox extends Sandbox { - - private String baseUrl; - - public FilesystemSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000, null); + + private final String baseUrl; + + public FilesystemSandbox(SandboxService managerApi, String userId, String sessionId) { + this(managerApi, userId, sessionId, null, LocalFileSystemConfig.builder().build(), Map.of()); } - + public FilesystemSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout) { - this(managerApi, userId, sessionId, timeout, null); + String baseUrl) { + this(managerApi, userId, sessionId, baseUrl, LocalFileSystemConfig.builder().build(), Map.of()); } - + public FilesystemSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout, - String baseUrl) { - super(managerApi, userId, sessionId, SandboxType.FILESYSTEM, timeout); + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, null, fileSystemConfig, Map.of()); + } + + public FilesystemSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment) { + this(managerApi, userId, sessionId, null, LocalFileSystemConfig.builder().build(), environment); + } + + public FilesystemSandbox( + SandboxService managerApi, + String userId, + String sessionId, + String baseUrl, + FileSystemConfig fileSystemConfig, + Map environment) { + super(managerApi, userId, sessionId, "filesystem", fileSystemConfig, environment); this.baseUrl = baseUrl; } - + /** * Get the desktop URL for VNC access. * This method provides GUI mixin functionality. @@ -66,30 +84,30 @@ public FilesystemSandbox( public String getDesktopUrl() { return GuiMixin.getDesktopUrl(managerApi, sandboxId, baseUrl); } - + public String readFile(String path) { Map arguments = new HashMap<>(); arguments.put("path", path); return callTool("read_file", arguments); } - + public String readMultipleFiles(List paths) { Map arguments = new HashMap<>(); arguments.put("paths", paths); return callTool("read_multiple_files", arguments); } - + public String writeFile(String path, String content) { Map arguments = new HashMap<>(); arguments.put("path", path); arguments.put("content", content); return callTool("write_file", arguments); } - + public String editFile(String path, Object[] edits) { return editFile(path, edits, false); } - + public String editFile(String path, Object[] edits, boolean dryRun) { Map arguments = new HashMap<>(); arguments.put("path", path); @@ -97,36 +115,36 @@ public String editFile(String path, Object[] edits, boolean dryRun) { arguments.put("dryRun", dryRun); return callTool("edit_file", arguments); } - + public String createDirectory(String path) { Map arguments = new HashMap<>(); arguments.put("path", path); return callTool("create_directory", arguments); } - + public String listDirectory(String path) { Map arguments = new HashMap<>(); arguments.put("path", path); return callTool("list_directory", arguments); } - + public String directoryTree(String path) { Map arguments = new HashMap<>(); arguments.put("path", path); return callTool("directory_tree", arguments); } - + public String moveFile(String source, String destination) { Map arguments = new HashMap<>(); arguments.put("source", source); arguments.put("destination", destination); return callTool("move_file", arguments); } - + public String searchFiles(String path, String pattern) { return searchFiles(path, pattern, new String[0]); } - + public String searchFiles(String path, String pattern, String[] excludePatterns) { Map arguments = new HashMap<>(); arguments.put("path", path); @@ -134,13 +152,13 @@ public String searchFiles(String path, String pattern, String[] excludePatterns) arguments.put("excludePatterns", excludePatterns != null ? excludePatterns : new String[0]); return callTool("search_files", arguments); } - + public String getFileInfo(String path) { Map arguments = new HashMap<>(); arguments.put("path", path); return callTool("get_file_info", arguments); } - + public String listAllowedDirectories() { return callTool("list_allowed_directories", new HashMap<>()); } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/GuiMixin.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/GuiMixin.java similarity index 58% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/GuiMixin.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/GuiMixin.java index 0fe8defd..d2841d75 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/GuiMixin.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/GuiMixin.java @@ -15,10 +15,10 @@ */ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; + +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -32,15 +32,15 @@ public class GuiMixin { /** * Get the desktop URL for VNC access. * This method should be called from a Sandbox instance that has access to - * SandboxManager and sandboxId. + * SandboxService and sandboxId. * - * @param managerApi The SandboxManager instance + * @param managerApi The SandboxService instance * @param sandboxId The sandbox ID * @param baseUrl Optional base URL (can be null) * @return The desktop URL for VNC access * @throws RuntimeException if sandbox is not healthy or info cannot be retrieved */ - public static String getDesktopUrl(SandboxManager managerApi, String sandboxId, String baseUrl) { + public static String getDesktopUrl(SandboxService managerApi, String sandboxId, String baseUrl) { // Check if sandbox is healthy by attempting to get info ContainerModel info; try { @@ -60,33 +60,28 @@ public static String getDesktopUrl(SandboxManager managerApi, String sandboxId, String path = "/vnc/vnc_lite.html"; String remotePath = "/vnc/vnc_relay.html"; - - try { - String encodedPassword = URLEncoder.encode(runtimeToken, StandardCharsets.UTF_8.toString()); - String params = "password=" + encodedPassword; - - if (baseUrl == null || baseUrl.isEmpty()) { - // Use direct URL from container info - String containerUrl = info.getBaseUrl(); - if (containerUrl == null || containerUrl.isEmpty()) { - throw new RuntimeException("Sandbox " + sandboxId + " does not have a base URL"); - } - // Ensure URL ends with / if not present - if (!containerUrl.endsWith("/")) { - containerUrl += "/"; - } - if(containerUrl.endsWith("fastapi/")){ - containerUrl = containerUrl.replace("fastapi/", ""); - } - return containerUrl + path.substring(1) + "?" + params; - } else { - // Use base_url with sandbox ID - String base = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; - return base + "/desktop/" + sandboxId + remotePath + "?" + params; + + String encodedPassword = URLEncoder.encode(runtimeToken, StandardCharsets.UTF_8); + String params = "password=" + encodedPassword; + + if (baseUrl == null || baseUrl.isEmpty()) { + // Use direct URL from container info + String containerUrl = info.getBaseUrl(); + if (containerUrl == null || containerUrl.isEmpty()) { + throw new RuntimeException("Sandbox " + sandboxId + " does not have a base URL"); + } + // Ensure URL ends with / if not present + if (!containerUrl.endsWith("/")) { + containerUrl += "/"; + } + if(containerUrl.endsWith("fastapi/")){ + containerUrl = containerUrl.replace("fastapi/", ""); } - } catch (UnsupportedEncodingException e) { - // Should never happen with UTF-8 - throw new RuntimeException("Failed to encode password", e); + return containerUrl + path.substring(1) + "?" + params; + } else { + // Use base_url with sandbox ID + String base = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + return base + "/desktop/" + sandboxId + remotePath + "?" + params; } } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/GuiSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/GuiSandbox.java similarity index 59% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/GuiSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/GuiSandbox.java index 38d07c26..bcd172b8 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/GuiSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/GuiSandbox.java @@ -15,8 +15,9 @@ */ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; import java.util.HashMap; @@ -28,34 +29,55 @@ * Provides VNC desktop access and computer use functionality. */ @RegisterSandbox( - imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-gui:latest", - sandboxType = SandboxType.GUI, - securityLevel = "high", - timeout = 60, - description = "GUI Sandbox" + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-gui:latest", + sandboxType = "gui", + securityLevel = "high", + timeout = 60, + description = "GUI Sandbox" ) public class GuiSandbox extends Sandbox { - - private String baseUrl; - - public GuiSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000, null); - } - - public GuiSandbox(SandboxManager managerApi, String userId, String sessionId, int timeout) { - this(managerApi, userId, sessionId, timeout, null); + + private final String baseUrl; + + public GuiSandbox(SandboxService managerApi, String userId, String sessionId) { + this(managerApi, userId, sessionId, null, LocalFileSystemConfig.builder().build(), Map.of()); } - + public GuiSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout, String baseUrl) { - super(managerApi, userId, sessionId, SandboxType.GUI, timeout); + this(managerApi, userId, sessionId, baseUrl, LocalFileSystemConfig.builder().build(), Map.of()); + } + + public GuiSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, null, fileSystemConfig, Map.of()); + } + + public GuiSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment) { + this(managerApi, userId, sessionId, null, LocalFileSystemConfig.builder().build(), environment); + } + + public GuiSandbox( + SandboxService managerApi, + String userId, + String sessionId, + String baseUrl, + FileSystemConfig fileSystemConfig, + Map environment) { + super(managerApi, userId, sessionId, "gui", fileSystemConfig, environment); this.baseUrl = baseUrl; } - + /** * Get the desktop URL for VNC access. * This method provides GUI mixin functionality. @@ -66,7 +88,7 @@ public GuiSandbox( public String getDesktopUrl() { return GuiMixin.getDesktopUrl(managerApi, sandboxId, baseUrl); } - + /** * Use computer mouse and keyboard to interact with the desktop. * @@ -76,24 +98,24 @@ public String getDesktopUrl() { public String computerUse(String action) { return computerUse(action, null, null); } - + /** * Use computer mouse and keyboard to interact with the desktop. * - * @param action The action to perform (key, type, mouse_move, left_click, etc.) + * @param action The action to perform (key, type, mouse_move, left_click, etc.) * @param coordinate The (x, y) coordinate for mouse actions * @return The result of the action */ public String computerUse(String action, List coordinate) { return computerUse(action, coordinate, null); } - + /** * Use computer mouse and keyboard to interact with the desktop. * - * @param action The action to perform (key, type, mouse_move, left_click, etc.) + * @param action The action to perform (key, type, mouse_move, left_click, etc.) * @param coordinate The (x, y) coordinate for mouse actions - * @param text Text to type for type action, or key command for key action + * @param text Text to type for type action, or key command for key action * @return The result of the action */ public String computerUse(String action, List coordinate, String text) { diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/MobileSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/MobileSandbox.java similarity index 70% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/MobileSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/MobileSandbox.java index 72f30b8b..457a856e 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/MobileSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/MobileSandbox.java @@ -16,9 +16,11 @@ package io.agentscope.runtime.sandbox.box; import io.agentscope.runtime.sandbox.box.model.HostPrerequisiteError; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +32,7 @@ @RegisterSandbox( imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-mobile:latest", - sandboxType = SandboxType.MOBILE, + sandboxType = "mobile", securityLevel = "high", timeout = 60, description = "Mobile Sandbox", @@ -40,16 +42,39 @@ public class MobileSandbox extends Sandbox { private static final Logger logger = LoggerFactory.getLogger(MobileSandbox.class); private static boolean hostCheckDone = false; - public MobileSandbox(SandboxManager managerApi, String userId, String sessionId) { - this(managerApi, userId, sessionId, 3000); + public MobileSandbox( + SandboxService managerApi, + String userId, + String sessionId + ) { + this(managerApi, userId, sessionId, Map.of()); + } + + public MobileSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig + ) { + this(managerApi, userId, sessionId, fileSystemConfig, Map.of()); + } + + public MobileSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment + ) { + this(managerApi, userId, sessionId, LocalFileSystemConfig.builder().build(), environment); } public MobileSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - int timeout) { - super(managerApi, userId, sessionId, SandboxType.MOBILE, timeout); + FileSystemConfig fileSystemConfig, + Map environment) { + super(managerApi, userId, sessionId, "mobile"); if (!hostCheckDone) { checkHostReadiness(); hostCheckDone = true; @@ -95,24 +120,24 @@ public void checkHostReadiness() { if (!missingDevices.isEmpty()) { String errorMessage = String.format(""" - - ========== HOST PREREQUISITE FAILED ========== - MobileSandbox requires specific kernel modules on the host machine. - The following required device files are missing: - - %s - - To fix this, please run the following commands on your Linux host: - - 1. Install extra kernel modules: - sudo apt update && sudo apt install -y linux-modules-extra-`uname -r` - - 2. Load modules and create device nodes: - sudo modprobe binder_linux devices="binder,hwbinder,vndbinder" - sudo modprobe ashmem_linux - - After running these commands, verify with: - ls -l /dev/binder* /dev/ashmem - ==================================================""", + + ========== HOST PREREQUISITE FAILED ========== + MobileSandbox requires specific kernel modules on the host machine. + The following required device files are missing: + - %s + + To fix this, please run the following commands on your Linux host: + + 1. Install extra kernel modules: + sudo apt update && sudo apt install -y linux-modules-extra-`uname -r` + + 2. Load modules and create device nodes: + sudo modprobe binder_linux devices="binder,hwbinder,vndbinder" + sudo modprobe ashmem_linux + + After running these commands, verify with: + ls -l /dev/binder* /dev/ashmem + ==================================================""", String.join(", ", missingDevices)); throw new HostPrerequisiteError(errorMessage); } @@ -128,15 +153,15 @@ public void checkHostReadiness() { * For actions involving coordinates, the values are absolute pixels, * with the origin (0, 0) at the top-left of the screen. * - * @param action The specific ADB action to perform. - * Examples: "tap", "swipe", "input_text", "key_event", - * "get_screenshot", "get_screen_resolution" + * @param action The specific ADB action to perform. + * Examples: "tap", "swipe", "input_text", "key_event", + * "get_screenshot", "get_screen_resolution" * @param coordinate The [x, y] coordinates for a "tap" action - * @param start The starting [x, y] coordinates for a "swipe" action - * @param end The ending [x, y] coordinates for a "swipe" action - * @param duration The duration of a "swipe" gesture in milliseconds - * @param code The key event code (e.g., 3) or name (e.g., "HOME") for the "key_event" action - * @param text The text string to be entered for the "input_text" action + * @param start The starting [x, y] coordinates for a "swipe" action + * @param end The ending [x, y] coordinates for a "swipe" action + * @param duration The duration of a "swipe" gesture in milliseconds + * @param code The key event code (e.g., 3) or name (e.g., "HOME") for the "key_event" action + * @param text The text string to be entered for the "input_text" action * @return The result of the ADB action */ public String adbUse( @@ -199,8 +224,8 @@ public String mobileTap(int x, int y) { /** * Perform a swipe gesture on the screen from a start point to an end point. * - * @param start The starting coordinates [x, y] in pixels - * @param end The ending coordinates [x, y] in pixels + * @param start The starting coordinates [x, y] in pixels + * @param end The ending coordinates [x, y] in pixels * @param duration The duration of the swipe in milliseconds (optional) * @return The result of the swipe action */ diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/Sandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/Sandbox.java new file mode 100644 index 00000000..94967b6a --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/Sandbox.java @@ -0,0 +1,207 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package io.agentscope.runtime.sandbox.box; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +public class Sandbox implements AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(Sandbox.class); + + protected SandboxService managerApi; + protected String sandboxId; + protected String userId; + protected String sessionId; + protected String sandboxType; + protected boolean closed = false; + protected Map environment; + protected FileSystemConfig fileSystemConfig; + + @JsonCreator + public Sandbox( + @JsonProperty("sandboxId") String sandboxId, + @JsonProperty("userId") String userId, + @JsonProperty("sessionId") String sessionId, + @JsonProperty("sandboxType") String sandboxType, + @JsonProperty("fileSystemConfig") FileSystemConfig fileSystemConfig, + @JsonProperty("environment") Map environment, + @JsonProperty("closed") boolean closed + ) { + this.sandboxId = sandboxId; + this.userId = userId; + this.sessionId = sessionId; + this.sandboxType = sandboxType; + this.fileSystemConfig = fileSystemConfig; + this.environment = environment != null ? new HashMap<>(environment) : new HashMap<>(); + this.closed = closed; + } + + public Sandbox(SandboxService managerApi, + String userId, + String sessionId, + String sandboxType + ) { + this(managerApi, userId, sessionId, sandboxType, LocalFileSystemConfig.builder().build(), Map.of()); + } + + public Sandbox(SandboxService managerApi, + String userId, + String sessionId, + String sandboxType, + FileSystemConfig fileSystemConfig + ) { + this(managerApi, userId, sessionId, sandboxType, fileSystemConfig, Map.of()); + } + + public Sandbox( + SandboxService managerApi, + String userId, + String sessionId, + String sandboxType, + Map environment + ) { + this(managerApi, userId, sessionId, sandboxType, LocalFileSystemConfig.builder().build(), environment); + } + + public Sandbox( + SandboxService managerApi, + String userId, + String sessionId, + String sandboxType, + FileSystemConfig fileSystemConfig, + Map environment + ) { + this.managerApi = managerApi; + this.userId = userId; + this.sessionId = sessionId; + this.sandboxType = sandboxType; + this.fileSystemConfig = fileSystemConfig; + this.environment = new HashMap<>(environment); + } + + public String getSandboxId() { + return sandboxId; + } + + public String getUserId() { + return userId; + } + + public String getSessionId() { + return sessionId; + } + + public String getSandboxType() { + return sandboxType; + } + + public Map getEnvironment() { + return environment; + } + + public FileSystemConfig getFileSystemStarter() { + return fileSystemConfig; + } + + private void initializeSandbox(){ + if (sandboxId == null || sandboxId.isEmpty()) { + try { + ContainerModel containerModel = managerApi.createContainer(this); + if (containerModel == null) { + throw new RuntimeException( + "No sandbox available. Please check if sandbox images exist." + ); + } + this.sandboxId = containerModel.getContainerId(); + logger.info("Sandbox initialized: {} (type={}, user={}, session={})", this.sandboxId, sandboxType, userId, sessionId); + } catch (Exception e) { + logger.error("Failed to initialize sandbox: {}", e.getMessage()); + throw new RuntimeException("Failed to initialize sandbox", e); + } + } + } + + @JsonIgnore + public ContainerModel getInfo() { + initializeSandbox(); + return managerApi.getInfo(sandboxId); + } + + public Map listTools() { + return listTools(null); + } + + public Map listTools(String toolType) { + initializeSandbox(); + return managerApi.listTools(sandboxId, toolType); + } + + public String callTool(String name, Map arguments) { + initializeSandbox(); + return managerApi.callTool(sandboxId, name, arguments); + } + + public Map addMcpServers(Map serverConfigs) { + return addMcpServers(serverConfigs, false); + } + + public Map addMcpServers(Map serverConfigs, boolean overwrite) { + initializeSandbox(); + return managerApi.addMcpServers(sandboxId, serverConfigs, overwrite); + } + + @Override + public void close() { + if (closed || sandboxId == null || sandboxId.isEmpty()) { + return; + } + + closed = true; + + try { + logger.info("Auto-releasing sandbox: {}", sandboxId); + if (!managerApi.stopAndRemoveSandbox(sandboxId)) { + logger.warn("Sandbox {} failed to remove", sandboxId); + } + } catch (Exception e) { + logger.error("Failed to cleanup sandbox: {}", e.getMessage()); + } + } + + /** + * Manually release sandbox resources + * Forces release of underlying container regardless of autoRelease setting + */ + public void release() { + close(); + } + + public boolean isClosed() { + return closed; + } +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/TrainingSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/TrainingSandbox.java similarity index 71% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/TrainingSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/TrainingSandbox.java index 35a2ce58..1148121d 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/box/TrainingSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/TrainingSandbox.java @@ -15,55 +15,71 @@ */ package io.agentscope.runtime.sandbox.box; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import java.util.HashMap; import java.util.Map; public class TrainingSandbox extends Sandbox { - + public TrainingSandbox(SandboxService managerApi, String userId, String sessionId, String sandboxType) { + this(managerApi, userId, sessionId, sandboxType, Map.of()); + } + + public TrainingSandbox( + SandboxService managerApi, + String userId, + String sessionId, + String sandboxType, + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, sandboxType, fileSystemConfig, Map.of()); + } + public TrainingSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - SandboxType sandboxType) { - this(managerApi, userId, sessionId, sandboxType, 3000); + String sandboxType, + Map environment) { + this(managerApi, userId, sessionId, sandboxType, LocalFileSystemConfig.builder().build(), environment); } - + public TrainingSandbox( - SandboxManager managerApi, + SandboxService managerApi, String userId, String sessionId, - SandboxType sandboxType, - int timeout) { - super(managerApi, userId, sessionId, sandboxType, timeout); + String sandboxType, + FileSystemConfig fileSystemConfig, + Map environment + ) { + super(managerApi, userId, sessionId, sandboxType, fileSystemConfig, environment); } - + /** * Create a new instance of a training environment - * + * * @param envType Type of environment to create - * @param taskId Identifier for the specific task + * @param taskId Identifier for the specific task * @return The created instance details */ public String createInstance(String envType, String taskId) { return createInstance(envType, taskId, null, null); } - + /** * Create a new instance of a training environment - * - * @param envType Type of environment to create - * @param taskId Identifier for the specific task + * + * @param envType Type of environment to create + * @param taskId Identifier for the specific task * @param instanceId Custom instance identifier (optional) - * @param params Additional parameters for instance creation (optional) + * @param params Additional parameters for instance creation (optional) * @return The created instance details */ public String createInstance( - String envType, - String taskId, - String instanceId, + String envType, + String taskId, + String instanceId, Map params) { Map arguments = new HashMap<>(); arguments.put("env_type", envType); @@ -76,23 +92,23 @@ public String createInstance( } return callTool("create_instance", arguments); } - + /** * Retrieve task identifiers for a specific environment - * + * * @param envType Type of environment * @return List of task identifiers */ public String getTaskIds(String envType) { return getTaskIds(envType, "train", null); } - + /** * Retrieve task identifiers for a specific environment - * + * * @param envType Type of environment - * @param split Data split to retrieve tasks from (default: "train") - * @param params Additional filtering parameters (optional) + * @param split Data split to retrieve tasks from (default: "train") + * @param params Additional filtering parameters (optional) * @return List of task identifiers */ public String getTaskIds(String envType, String split, Map params) { @@ -104,23 +120,23 @@ public String getTaskIds(String envType, String split, Map param } return callTool("get_task_ids", arguments); } - + /** * Retrieve the environment profile - * + * * @param envType Type of environment * @return Environment profile details */ public String getEnvProfile(String envType) { return getEnvProfile(envType, "train", null); } - + /** * Retrieve the environment profile - * + * * @param envType Type of environment - * @param split Data split to retrieve profile from (default: "train") - * @param params Additional profile retrieval parameters (optional) + * @param split Data split to retrieve profile from (default: "train") + * @param params Additional profile retrieval parameters (optional) * @return Environment profile details */ public String getEnvProfile(String envType, String split, Map params) { @@ -132,23 +148,23 @@ public String getEnvProfile(String envType, String split, Map pa } return callTool("get_env_profile", arguments); } - + /** * Execute a step in the training environment - * + * * @param instanceId Identifier of the environment instance * @return Result of the step execution */ public String step(String instanceId) { return step(instanceId, null, null); } - + /** * Execute a step in the training environment - * + * * @param instanceId Identifier of the environment instance - * @param action Action to be performed in the environment (optional) - * @param params Additional step parameters (optional) + * @param action Action to be performed in the environment (optional) + * @param params Additional step parameters (optional) * @return Result of the step execution */ public String step(String instanceId, Map action, Map params) { @@ -158,28 +174,28 @@ public String step(String instanceId, Map action, Map()); return callTool("step", arguments); } - + /** * Evaluate the performance of a training environment instance - * + * * @param instanceId Identifier of the environment instance * @return Evaluation results */ public String evaluate(String instanceId) { return evaluate(instanceId, null, null); } - + /** * Evaluate the performance of a training environment instance - * + * * @param instanceId Identifier of the environment instance - * @param messages Evaluation-related messages (optional) - * @param params Additional evaluation parameters (optional) + * @param messages Evaluation-related messages (optional) + * @param params Additional evaluation parameters (optional) * @return Evaluation results */ public String evaluate( - String instanceId, - Map messages, + String instanceId, + Map messages, Map params) { Map arguments = new HashMap<>(); arguments.put("instance_id", instanceId); @@ -187,10 +203,10 @@ public String evaluate( arguments.put("params", params != null ? params : new HashMap<>()); return callTool("evaluate", arguments); } - + /** * Release a training environment instance - * + * * @param instanceId Identifier of the instance to be released * @return Result of the instance release operation */ diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/WebShopSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/WebShopSandbox.java new file mode 100644 index 00000000..9552e4f1 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/WebShopSandbox.java @@ -0,0 +1,67 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.agentscope.runtime.sandbox.box; + +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; +import io.agentscope.runtime.sandbox.manager.registry.RegisterSandbox; + +import java.util.Map; + +@RegisterSandbox( + imageName = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-webshop:latest", + sandboxType = "webshop", + runtimeConfig = {"shm_size=5.06gb"}, + securityLevel = "medium", + timeout = 30, + description = "webshop Sandbox" +) +public class WebShopSandbox extends TrainingSandbox { + public WebShopSandbox( + SandboxService managerApi, + String userId, + String sessionId) { + this(managerApi, userId, sessionId, Map.of()); + } + + public WebShopSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig) { + this(managerApi, userId, sessionId, fileSystemConfig, Map.of()); + } + + public WebShopSandbox( + SandboxService managerApi, + String userId, + String sessionId, + Map environment) { + this(managerApi, userId, sessionId, LocalFileSystemConfig.builder().build(), environment); + } + + public WebShopSandbox( + SandboxService managerApi, + String userId, + String sessionId, + FileSystemConfig fileSystemConfig, + Map environment + ) { + super(managerApi, userId, sessionId, "webshop", fileSystemConfig, environment); + } +} + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/box/model/HostPrerequisiteError.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/model/HostPrerequisiteError.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/box/model/HostPrerequisiteError.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/box/model/HostPrerequisiteError.java diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/ManagerConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/ManagerConfig.java new file mode 100644 index 00000000..c88bea6e --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/ManagerConfig.java @@ -0,0 +1,129 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.manager; + +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; +import io.agentscope.runtime.sandbox.manager.model.container.PortRange; +import io.agentscope.runtime.sandbox.manager.utils.InMemorySandboxMap; +import io.agentscope.runtime.sandbox.manager.utils.SandboxMap; + +public class ManagerConfig { + private final String containerPrefixKey; + private final PortRange portRange; + private final BaseClientStarter clientStarter; + private final SandboxMap sandboxMap; // SandboxKey -> ContainerModel, managed in SandboxService + + private final String baseUrl; + private final String bearerToken; + + private final String agentBayApiKey; + + private ManagerConfig(Builder builder) { + this.containerPrefixKey = builder.containerPrefixKey; + this.portRange = builder.portRange; + this.clientStarter = builder.clientStarter; + this.sandboxMap = builder.sandboxMap; + this.baseUrl = builder.baseUrl; + this.bearerToken = builder.bearerToken; + this.agentBayApiKey = builder.agentBayApiKey; + } + + public String getContainerPrefixKey() { + return containerPrefixKey; + } + + public PortRange getPortRange() { + return portRange; + } + + public BaseClientStarter getClientStarter() { + return clientStarter; + } + + public SandboxMap getSandboxMap() { + return sandboxMap; + } + + public String getBaseUrl() { + return baseUrl; + } + + public String getBearerToken() { + return bearerToken; + } + + public String getAgentBayApiKey() { + return agentBayApiKey; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String containerPrefixKey = "sandbox_container_"; + private PortRange portRange = new PortRange(49152, 59152); + ; + private BaseClientStarter clientStarter = DockerClientStarter.builder().build(); + private SandboxMap sandboxMap = new InMemorySandboxMap(); + private String baseUrl; + private String bearerToken; + + private String agentBayApiKey; + + public Builder containerPrefixKey(String containerPrefixKey) { + this.containerPrefixKey = containerPrefixKey; + return this; + } + + public Builder portRange(PortRange portRange) { + this.portRange = portRange; + return this; + } + + public Builder clientStarter(BaseClientStarter clientStarter) { + this.clientStarter = clientStarter; + return this; + } + + public Builder sandboxMap(SandboxMap sandboxMap) { + this.sandboxMap = sandboxMap; + return this; + } + + public Builder baseUrl(String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + public Builder bearerToken(String bearerToken) { + this.bearerToken = bearerToken; + return this; + } + + public Builder agentBayApiKey(String agentBayApiKey) { + this.agentBayApiKey = agentBayApiKey; + return this; + } + + public ManagerConfig build() { + return new ManagerConfig(this); + } + } + +} diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/SandboxService.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/SandboxService.java new file mode 100644 index 00000000..eb8d6ca5 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/SandboxService.java @@ -0,0 +1,569 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.manager; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.agentscope.runtime.sandbox.box.AgentBaySandbox; +import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.ContainerCreateResult; +import io.agentscope.runtime.sandbox.manager.client.container.agentbay.AgentBayClient; +import io.agentscope.runtime.sandbox.manager.client.sandbox.SandboxClient; +import io.agentscope.runtime.sandbox.manager.client.sandbox.SandboxHttpClient; +import io.agentscope.runtime.sandbox.manager.client.sandbox.TrainingSandboxClient; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.StorageManager; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerClientType; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import io.agentscope.runtime.sandbox.manager.model.container.SandboxKey; +import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; +import io.agentscope.runtime.sandbox.manager.model.sandbox.SandboxConfig; +import io.agentscope.runtime.sandbox.manager.registry.SandboxRegistryService; +import io.agentscope.runtime.sandbox.manager.remote.RemoteHttpClient; +import io.agentscope.runtime.sandbox.manager.remote.RemoteWrapper; +import io.agentscope.runtime.sandbox.manager.remote.RequestMethod; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; +import io.agentscope.runtime.sandbox.manager.utils.RandomStringGenerator; +import io.agentscope.runtime.sandbox.manager.utils.SandboxMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.file.Paths; +import java.util.*; + +public class SandboxService implements AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(SandboxService.class); + private final ManagerConfig managerConfig; + private BaseClient containerClient; + private final SandboxMap sandboxMap; + private static final String BROWSER_SESSION_ID = "123e4567-e89b-12d3-a456-426614174000"; + private final RemoteHttpClient remoteHttpClient; + private AgentBayClient agentBayClient; + + public SandboxService(ManagerConfig managerConfig) { + this.managerConfig = managerConfig; + this.sandboxMap = managerConfig.getSandboxMap(); + if (managerConfig.getBaseUrl() != null && !managerConfig.getBaseUrl().isEmpty()) { + this.remoteHttpClient = new RemoteHttpClient(managerConfig.getBaseUrl(), managerConfig.getBearerToken()); + logger.info("Initialized SandboxService in remote mode with base URL: {}", managerConfig.getBaseUrl()); + } else { + this.remoteHttpClient = null; + logger.info("RemoteHttpClient not initialized: baseUrl is null or empty"); + } + } + + public void start() { + logger.info("Initializing SandboxService with container manager: {}", this.managerConfig.getClientStarter().getContainerClientType()); + this.containerClient = this.managerConfig.getClientStarter().startClient(new PortManager(this.managerConfig.getPortRange())); + if (managerConfig.getAgentBayApiKey() != null && !managerConfig.getAgentBayApiKey().isEmpty()) { + agentBayClient = new AgentBayClient(managerConfig.getAgentBayApiKey()); + } + logger.info("SandboxService started."); + } + + public String createAgentBayContainer(AgentBaySandbox sandbox) { + if (agentBayClient == null) { + throw new RuntimeException("AgentBay client is not initialized."); + } + ContainerCreateResult createResult = agentBayClient.createContainer(sandbox.getImageId(), sandbox.getLabels()); + String agentBayId = "agentbay_" + sandbox.getImageId() + "_" + sandbox.getLabels().toString(); + ContainerModel containerModel = ContainerModel.builder().containerId(createResult.getContainerId()).containerName(agentBayId).build(); + sandboxMap.addSandbox(new SandboxKey(sandbox.getUserId(), sandbox.getSessionId(), agentBayId), containerModel); + return createResult.getContainerId(); + } + + @RemoteWrapper + public ContainerModel createContainer(Sandbox sandbox) throws JsonProcessingException { + if (this.remoteHttpClient != null) { + logger.info("Creating container in remote mode via RemoteHttpClient"); + ObjectMapper mapper = new ObjectMapper(); + String sandboxJson = mapper.writeValueAsString(sandbox); + Map request = Map.of("sandbox", sandboxJson); + Object result = remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/createContainer", + request, + "data" + ); + if (result instanceof Map) { + @SuppressWarnings("unchecked") + Map resultMap = (Map) result; + ContainerModel containerModel = ContainerModel.fromMap(resultMap); + sandboxMap.addSandbox(new SandboxKey(sandbox.getUserId(), sandbox.getSessionId(), sandbox.getSandboxType()), containerModel); + return containerModel; + } + return null; + } + + if (sandboxMap.containSandbox(new SandboxKey(sandbox.getUserId(), sandbox.getSessionId(), sandbox.getSandboxType()))) { + return sandboxMap.getSandbox(new SandboxKey(sandbox.getUserId(), sandbox.getSessionId(), sandbox.getSandboxType())); + } + + Map environment = sandbox.getEnvironment(); + FileSystemConfig fileSystemConfig = sandbox.getFileSystemStarter(); + String sandboxType = sandbox.getSandboxType(); + ContainerClientType containerClientType = managerConfig.getClientStarter().getContainerClientType(); + StorageManager storageManager = fileSystemConfig.createStorageManager(); + + String containerName; + String prefix = managerConfig.getContainerPrefixKey(); + if (prefix == null || prefix.isEmpty()) { + prefix = "sandbox"; + } + + for (Map.Entry entry : environment.entrySet()) { + String value = entry.getValue(); + if (value == null) { + logger.warn("Environment variable {} has null value", entry.getKey()); + return null; + } + } + + String workdir = "/workspace"; + String default_mount_dir = fileSystemConfig.getMountDir(); + String[] portsArray = {"80/tcp"}; + List ports = Arrays.asList(portsArray); + String imageName = SandboxRegistryService.getImageByType(sandboxType).orElse("agentscope-registry.ap-southeast-1.cr.aliyuncs.com/agentscope/runtime-sandbox-base:latest"); + SandboxConfig sandboxConfig = SandboxRegistryService.getConfigByType(sandboxType).orElse(null); + if (sandboxConfig != null) { + logger.info("Using registered sandbox configuration: {}", sandboxConfig.getDescription()); + if (sandboxConfig.getEnvironment() != null && !sandboxConfig.getEnvironment().isEmpty()) { + Map mergedEnv = new HashMap<>(sandboxConfig.getEnvironment()); + mergedEnv.putAll(environment); + environment = mergedEnv; + } + } + + logger.info("Checking image: {}", imageName); + if (containerClientType == ContainerClientType.DOCKER) { + if (!containerClient.ensureImageAvailable(imageName)) { + logger.error("Can not get image: {}", imageName); + throw new RuntimeException("Pull image failed: " + imageName); + } + logger.info("Docker image is ready: {}", imageName); + } + + String sessionId = RandomStringGenerator.generateRandomString(22); + String currentDir = System.getProperty("user.dir"); + String mountDir = currentDir + "/" + default_mount_dir + "/" + sessionId; + + if (containerClientType == ContainerClientType.DOCKER) { + containerName = prefix + sessionId.toLowerCase(); + while(containerClient.containerNameExists(containerName)){ + sessionId = RandomStringGenerator.generateRandomString(22); + containerName = prefix + sessionId.toLowerCase(); + } + } else { + containerName = prefix.replace('_', '-') + sessionId.toLowerCase(); + } + + if (containerClientType == ContainerClientType.AGENTRUN || containerClientType == ContainerClientType.FC) { + mountDir = Paths.get(mountDir).toAbsolutePath().toString(); + } + File file = new File(mountDir); + if (!file.exists()) { + boolean ignored = file.mkdirs(); + } + String storagePath = sandbox.getFileSystemStarter().getStorageFolderPath(); + // Todo:Currently using global storage path if not provided, still need to wait for next movement of python version + if (!mountDir.isEmpty() && !storagePath.isEmpty() && containerClientType != ContainerClientType.AGENTRUN && containerClientType != ContainerClientType.FC) { + logger.info("Downloading from storage path: {} to mount dir: {}", storagePath, mountDir); + boolean downloadSuccess = storageManager.downloadFolder(storagePath, mountDir); + if (downloadSuccess) { + logger.info("Successfully downloaded files from storage"); + } else { + logger.warn("Failed to download files from storage, continuing with empty mount dir"); + } + } + String runtimeToken = RandomStringGenerator.generateRandomString(32); + environment.put("SECRET_TOKEN", runtimeToken); + List volumeBindings = new ArrayList<>(); + if (containerClientType != ContainerClientType.AGENTRUN && containerClientType != ContainerClientType.FC) { + volumeBindings.add(new VolumeBinding(mountDir, workdir, "rw")); + } + Map readonlyMounts = fileSystemConfig.getReadonlyMounts(); + if (readonlyMounts != null && !readonlyMounts.isEmpty()) { + logger.info("Adding readonly mounts: {} mount(s)", readonlyMounts.size()); + for (Map.Entry entry : readonlyMounts.entrySet()) { + String hostPath = entry.getKey(); + String containerPath = entry.getValue(); + if (!Paths.get(hostPath).isAbsolute()) { + hostPath = Paths.get(hostPath).toAbsolutePath().toString(); + logger.info("Converting relative path to absolute: {}", hostPath); + } + File hostFile = new File(hostPath); + if (!hostFile.exists()) { + logger.warn("Readonly mount host path does not exist: {}, skipping", hostPath); + continue; + } + volumeBindings.add(new VolumeBinding(hostPath, containerPath, "ro")); + logger.info("Added readonly mount: {} -> {}", hostPath, containerPath); + } + } + Map nonCopyMounts = fileSystemConfig.getNonCopyMount(); + if (nonCopyMounts != null && !nonCopyMounts.isEmpty()) { + logger.info("Adding non-copy mounts: {} mount(s)", nonCopyMounts.size()); + for (Map.Entry entry : nonCopyMounts.entrySet()) { + String hostPath = entry.getKey(); + String containerPath = entry.getValue(); + if (!Paths.get(hostPath).isAbsolute()) { + hostPath = Paths.get(hostPath).toAbsolutePath().toString(); + logger.info("Converting relative path to absolute: {}", hostPath); + } + File hostFile = new File(hostPath); + if (!hostFile.exists()) { + logger.warn("NonCopy mount host path does not exist: {}, skipping", hostPath); + continue; + } + volumeBindings.add(new VolumeBinding(hostPath, containerPath, "rw")); + logger.info("Added non Copy mount: {} -> {}", hostPath, containerPath); + } + } + + Map runtimeConfig = Map.of(); + if (sandboxConfig != null) { + runtimeConfig = sandboxConfig.getRuntimeConfig(); + } + logger.info("Runtime config: {}", runtimeConfig); + ContainerCreateResult createResult; + createResult = containerClient.createContainer(containerName, imageName, ports, volumeBindings, environment, runtimeConfig); + + String containerId = createResult.getContainerId(); + if (containerId == null) { + logger.error("Container creation failed: containerId is null"); + return null; + } + List resultPorts = createResult.getPorts(); + String ip = createResult.getIp(); + String httpProtocol = createResult.getProtocol(); + + String accessPort = resultPorts != null && !resultPorts.isEmpty() ? resultPorts.get(0) : "80"; + + String baseHost = ip != null ? ip : "localhost"; + + String[] mappedPorts = resultPorts != null ? resultPorts.toArray(new String[0]) : new String[]{accessPort}; + + ContainerModel containerModel = ContainerModel.builder() + .sessionId(sessionId) + .containerId(containerId) + .containerName(containerName) + .baseUrl(String.format("%s://%s:%s/fastapi", httpProtocol, baseHost, accessPort)) + .browserUrl(String.format("%s://%s:%s/steel-api/%s", httpProtocol, baseHost, accessPort, runtimeToken)) + .frontBrowserWS(String.format("ws://%s:%s/steel-api/%s/v1/sessions/cast", baseHost, accessPort, runtimeToken)) + .clientBrowserWS(String.format("ws://%s:%s/steel-api/%s/&sessionId=%s", baseHost, accessPort, runtimeToken, BROWSER_SESSION_ID)) + .artifactsSIO(String.format("%s://%s:%s/v1", httpProtocol, baseHost, accessPort)) + .ports(mappedPorts) + .mountDir(mountDir) + .storagePath(storagePath) + .runtimeToken(runtimeToken) + .authToken(runtimeToken) + .version(imageName) + .build(); + + logger.info("Created Container: {}", containerModel); + + containerClient.startContainer(containerId); + sandboxMap.addSandbox(new SandboxKey(sandbox.getUserId(), sandbox.getSessionId(), sandbox.getSandboxType()), containerModel); + return containerModel; + } + + public ContainerModel getSandbox(String containerId) { + return sandboxMap.getSandbox(containerId); + } + + public ContainerModel getSandbox(String userId, String sessionId, String sandboxType) { + return sandboxMap.getSandbox(new SandboxKey(userId, sessionId, sandboxType)); + } + + public boolean startSandbox(String userId, String sessionId, String sandboxType) { + return startSandbox(sandboxMap.getSandbox(new SandboxKey(userId, sessionId, sandboxType))); + } + + public boolean startSandbox(String containerId) { + return startSandbox(sandboxMap.getSandbox(containerId)); + } + + @RemoteWrapper + public boolean startSandbox(ContainerModel containerModel) { + if (this.remoteHttpClient != null) { + logger.info("Starting sandbox in remote mode via RemoteHttpClient"); + Map request = Map.of("containerModel", containerModel); + remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/startSandbox", + request, + "data" + ); + return true; + } + if (containerModel == null) { + return false; + } + containerClient.startContainer(containerModel.getContainerId()); + logger.info("Container status updated to: running"); + return true; + } + + public void stopSandbox(String userId, String sessionId, String sandboxType) { + stopSandbox(sandboxMap.getSandbox(new SandboxKey(userId, sessionId, sandboxType))); + } + + public void stopSandbox(String containerId) { + stopSandbox(sandboxMap.getSandbox(containerId)); + } + + @RemoteWrapper + public void stopSandbox(ContainerModel containerModel) { + if(containerModel.getContainerName().startsWith("agentbay_")) { + logger.info("Stopping sandbox managed by AgentBay via AgentBayClient"); + agentBayClient.stopContainer(containerModel.getContainerId()); + return; + } + if (this.remoteHttpClient != null) { + logger.info("Stopping sandbox in remote mode via RemoteHttpClient"); + Map request = Map.of("containerModel", containerModel); + remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/stopSandbox", + request, + "data" + ); + return; + } + containerClient.stopContainer(containerModel.getContainerId()); + logger.info("Container status updated to: stopped"); + } + + public boolean removeSandbox(String userId, String sessionId, String sandboxType) { + return removeSandbox(sandboxMap.getSandbox(new SandboxKey(userId, sessionId, sandboxType))); + } + + public boolean removeSandbox(String containerId) { + return removeSandbox(sandboxMap.getSandbox(containerId)); + } + + @RemoteWrapper + public boolean removeSandbox(ContainerModel containerModel) { + if(containerModel.getContainerName().startsWith("agentbay_")) { + logger.warn("AgentBay sandbox can only be stopped, not removed via AgentBayClient"); + return true; + } + if (this.remoteHttpClient != null) { + logger.info("Removing sandbox in remote mode via RemoteHttpClient"); + Map request = Map.of("containerModel", containerModel); + remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/removeSandbox", + request, + "data" + ); + return true; + } + containerClient.removeContainer(containerModel.getContainerId()); + logger.info("Container removed: {}", containerModel.getContainerId()); + sandboxMap.removeSandbox(containerModel.getContainerId()); + return true; + } + + public boolean stopAndRemoveSandbox(String userId, String sessionId, String sandboxType) { + stopSandbox(userId, sessionId, sandboxType); + return removeSandbox(userId, sessionId, sandboxType); + } + + public boolean stopAndRemoveSandbox(String containerId) { + stopSandbox(containerId); + return removeSandbox(containerId); + } + + public boolean release(String containerId) { + return stopAndRemoveSandbox(containerId); + } + + public String getSandboxStatus(String userId, String sessionId, String sandboxType) { + return getSandboxStatus(sandboxMap.getSandbox(new SandboxKey(userId, sessionId, sandboxType))); + } + + public String getSandboxStatus(String containerId) { + return getSandboxStatus(sandboxMap.getSandbox(containerId)); + } + + @RemoteWrapper + public String getSandboxStatus(ContainerModel containerModel) { + if (this.remoteHttpClient != null) { + logger.info("Getting sandbox status in remote mode via RemoteHttpClient"); + Map request = Map.of("containerModel", containerModel); + Object result = remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/getSandboxStatus", + request, + "data" + ); + if (result instanceof String) { + return (String) result; + } + return "unknown"; + } + if (containerModel == null) { + return "not_found"; + } + return containerClient.getContainerStatus(containerModel.getContainerId()); + } + + public Map getAllSandboxes() { + return sandboxMap.getAllSandboxes(); + } + + @RemoteWrapper + public ContainerModel getInfo(String containerId) { + if (this.remoteHttpClient != null) { + logger.info("Getting sandbox info in remote mode via RemoteHttpClient"); + Map request = Map.of("containerId", containerId); + Object result = remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/getInfo", + request, + "data" + ); + if (result instanceof Map) { + @SuppressWarnings("unchecked") + Map resultMap = (Map) result; + return ContainerModel.fromMap(resultMap); + } + return null; + } + return sandboxMap.getSandbox(containerId); + } + + public void cleanupAllSandboxes() { + for (String containerId : sandboxMap.getAllSandboxes().keySet()) { + if (!removeSandbox(containerId)) { + logger.warn("Error cleaning up container {}", containerId); + } + } + } + + @Override + public void close() { + cleanupAllSandboxes(); + } + + private SandboxClient establishConnection(String sandboxId) { + try { + ContainerModel containerInfo = getInfo(sandboxId); + if (containerInfo.getVersion().contains("sandbox-appworld") || containerInfo.getVersion().contains("sandbox-bfcl")) { + return new TrainingSandboxClient(containerInfo, 60); + } + return new SandboxHttpClient(containerInfo, 60); + } catch (Exception e) { + logger.error("Failed to establish connection to sandbox: {}", e.getMessage()); + throw new RuntimeException("Failed to establish connection", e); + } + } + + @RemoteWrapper + public Map listTools(String sandboxId, String toolType) { + if (this.remoteHttpClient != null) { + logger.info("Listing tools in remote mode via RemoteHttpClient"); + Map request = Map.of( + "sandboxId", sandboxId, + "toolType", toolType + ); + Object result = remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/listTools", + request, + "data" + ); + if (result instanceof Map) { + @SuppressWarnings("unchecked") + Map resultMap = (Map) result; + return resultMap; + } + return new HashMap<>(); + } + try (SandboxClient client = establishConnection(sandboxId)) { + return client.listTools(toolType, Map.of()); + } catch (Exception e) { + logger.error("Error listing tools: {}", e.getMessage()); + return new HashMap<>(); + } + } + + @RemoteWrapper + public String callTool(String sandboxId, String toolName, Map arguments) { + if (this.remoteHttpClient != null) { + logger.info("Calling tool in remote mode via RemoteHttpClient"); + Map request = Map.of( + "sandboxId", sandboxId, + "toolName", toolName, + "arguments", arguments + ); + Object result = remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/callTool", + request, + "data" + ); + if (result instanceof String) { + return (String) result; + } + return "{\"isError\":true,\"content\":[{\"type\":\"text\",\"text\":\"Invalid response from remote callTool\"}]}"; + } + try (SandboxClient client = establishConnection(sandboxId)) { + return client.callTool(toolName, arguments); + } catch (Exception e) { + logger.error("Error calling tool {}: {}", toolName, e.getMessage()); + return "{\"isError\":true,\"content\":[{\"type\":\"text\",\"text\":\"Error calling tool: " + e.getMessage() + "\"}]}"; + } + } + + @RemoteWrapper + public Map addMcpServers(String sandboxId, Map serverConfigs, boolean overwrite) { + if (this.remoteHttpClient != null) { + logger.info("Adding MCP servers in remote mode via RemoteHttpClient"); + Map request = Map.of( + "sandboxId", sandboxId, + "serverConfigs", serverConfigs, + "overwrite", overwrite + ); + Object result = remoteHttpClient.makeRequest( + RequestMethod.POST, + "/sandbox/addMcpServers", + request, + "data" + ); + if (result instanceof Map) { + @SuppressWarnings("unchecked") + Map resultMap = (Map) result; + return resultMap; + } + return new HashMap<>(); + } + try (SandboxClient client = establishConnection(sandboxId)) { + return client.addMcpServers(serverConfigs, overwrite); + } catch (Exception e) { + logger.error("Error adding MCP servers: {}", e.getMessage()); + return new HashMap<>(); + } + } + + public AgentBayClient getAgentBayClient() { + return agentBayClient; + } +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/BaseClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/BaseClient.java similarity index 88% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/BaseClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/BaseClient.java index ef4cf9ae..5198ec3a 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/BaseClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/BaseClient.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; + +package io.agentscope.runtime.sandbox.manager.client.container; + import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; @@ -25,13 +27,13 @@ * Defines basic interfaces for container management, supports Docker and Kubernetes implementations */ public abstract class BaseClient { - + /** * Connect to container management service * @return connection status */ public abstract boolean connect(); - + /** * Create container * @param containerName container name @@ -43,35 +45,35 @@ public abstract class BaseClient { * @return ContainerCreateResult containing containerId, ports, ip, and optional protocol */ public abstract ContainerCreateResult createContainer(String containerName, String imageName, - List ports, - List volumeBindings, - Map environment, Map runtimeConfig); - + List ports, + List volumeBindings, + Map environment, Map runtimeConfig); + /** * Start container * @param containerId container ID */ public abstract void startContainer(String containerId); - + /** * Stop container * @param containerId container ID */ public abstract void stopContainer(String containerId); - + /** * Remove container * @param containerId container ID */ public abstract void removeContainer(String containerId); - + /** * Get container status * @param containerId container ID * @return container status */ public abstract String getContainerStatus(String containerId); - + /** * Stop and remove container * @param containerId container ID @@ -80,34 +82,34 @@ public void stopAndRemoveContainer(String containerId) { stopContainer(containerId); removeContainer(containerId); } - + /** * Check connection status * @return whether connected */ public abstract boolean isConnected(); - + /** * Check if image exists locally * @param imageName image name * @return whether image exists locally */ public abstract boolean imageExists(String imageName); - + /** * Inspect container to check if it exists * @param containerIdOrName container ID or name * @return true if container exists, false otherwise */ public abstract boolean inspectContainer(String containerIdOrName); - + /** * Pull image from registry * @param imageName image name * @return whether pull was successful */ public abstract boolean pullImage(String imageName); - + /** * Ensure image is available (check and pull if needed) * @param imageName image name @@ -119,4 +121,8 @@ public boolean ensureImageAvailable(String imageName) { } return pullImage(imageName); } + + public boolean containerNameExists(String containerName){ + return false; + } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/BaseClientConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/BaseClientStarter.java similarity index 56% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/BaseClientConfig.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/BaseClientStarter.java index 9707e430..4cf89631 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/BaseClientConfig.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/BaseClientStarter.java @@ -14,26 +14,21 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client.config; +package io.agentscope.runtime.sandbox.manager.client.container; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerManagerType; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerClientType; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; -public class BaseClientConfig { - private ContainerManagerType clientType; - - public BaseClientConfig() { - this(ContainerManagerType.DOCKER); +public abstract class BaseClientStarter { + private final ContainerClientType containerClientType; + + public ContainerClientType getContainerClientType() { + return containerClientType; } - - public BaseClientConfig(ContainerManagerType clientType) { - this.clientType = clientType; + + public BaseClientStarter(ContainerClientType containerClientType){ + this.containerClientType = containerClientType; } - public ContainerManagerType getClientType() { - return clientType; - } - - public void setClientType(ContainerManagerType clientType) { - this.clientType = clientType; - } + public abstract BaseClient startClient(PortManager portManager); } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/ContainerCreateResult.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/ContainerCreateResult.java similarity index 97% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/ContainerCreateResult.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/ContainerCreateResult.java index 4c9f38fe..87b99ccb 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/ContainerCreateResult.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/ContainerCreateResult.java @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; + +package io.agentscope.runtime.sandbox.manager.client.container; import java.util.ArrayList; import java.util.Arrays; @@ -96,4 +97,3 @@ public String getProtocol() { } } - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/AgentBayClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/agentbay/AgentBayClient.java similarity index 95% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/AgentBayClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/agentbay/AgentBayClient.java index 692d5bce..5c0046f1 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/AgentBayClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/agentbay/AgentBayClient.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; +package io.agentscope.runtime.sandbox.manager.client.container.agentbay; import com.aliyun.agentbay.AgentBay; import com.aliyun.agentbay.exception.AgentBayException; @@ -23,6 +23,8 @@ import com.aliyun.agentbay.model.SessionResult; import com.aliyun.agentbay.session.CreateSessionParams; import com.aliyun.agentbay.session.Session; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.ContainerCreateResult; import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +32,11 @@ import java.util.List; import java.util.Map; -public class AgentBayClient extends BaseClient{ +public class AgentBayClient extends BaseClient { private static final Logger logger = LoggerFactory.getLogger(AgentBayClient.class); private AgentBay agentBay; - private String apiKey; + private final String apiKey; public AgentBayClient(String apiKey){ this.apiKey = apiKey; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/DockerClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/docker/DockerClient.java similarity index 93% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/DockerClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/docker/DockerClient.java index a3d77763..e36d89ff 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/DockerClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/docker/DockerClient.java @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; + +package io.agentscope.runtime.sandbox.manager.client.container.docker; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.CreateContainerResponse; @@ -24,10 +25,11 @@ import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.ContainerCreateResult; import io.agentscope.runtime.sandbox.manager.model.container.PortRange; import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; -import io.agentscope.runtime.sandbox.manager.util.PortManager; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +40,7 @@ public class DockerClient extends BaseClient { private static final Logger logger = LoggerFactory.getLogger(DockerClient.class); private com.github.dockerjava.api.DockerClient client; private boolean connected = false; - private DockerClientConfig config; + private DockerClientStarter config; private PortManager portManager; private final Map> portsCache = new ConcurrentHashMap<>(); @@ -53,21 +55,15 @@ public DockerClient() { /** * Constructor with DockerClientConfig */ - public DockerClient(DockerClientConfig config) { + public DockerClient(DockerClientStarter config) { this(config, new PortManager(new PortRange())); } - public DockerClient(DockerClientConfig config, PortManager portManager) { + public DockerClient(DockerClientStarter config, PortManager portManager) { this.config = config; this.portManager = portManager; } - public void connectDocker() { - this.client = openDockerClient(); - this.client.infoCmd().exec(); - this.connected = true; - } - @Override public boolean connect() { try { @@ -239,35 +235,6 @@ private com.github.dockerjava.api.DockerClient openDockerClient() { return DockerClientImpl.getInstance(config, httpClient); } - public com.github.dockerjava.api.DockerClient connectDocker(String dockerInstance) { - var config = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost(dockerInstance) - .build(); - - DockerHttpClient httpClient = new ZerodepDockerHttpClient.Builder() - .dockerHost(config.getDockerHost()) - .sslConfig(config.getSSLConfig()) - .build(); - - com.github.dockerjava.api.DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient); - dockerClient.infoCmd().exec(); - return dockerClient; - } - - /** - * Create container (simple version) - * - * @param client Docker client - * @param containerName container name - * @param imageName image name - * @return: CreateContainerResponse - */ - public CreateContainerResponse createContainers(com.github.dockerjava.api.DockerClient client, String containerName, String imageName) { - return client.createContainerCmd(imageName) - .withName(containerName) - .exec(); - } - /** * Create container (supports port mapping) * @@ -701,4 +668,18 @@ public void onNext(PullResponseItem item) { return false; } } + + @Override + public boolean containerNameExists(String containerName){ + List containers = client.listContainersCmd() + .withShowAll(true) + .exec(); + + List names = containers.stream() + .flatMap(c -> Arrays.stream(c.getNames())) + .map(name -> name.startsWith("/") ? name.substring(1) : name) + .toList(); + + return names.contains(containerName); + } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/DockerClientConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/docker/DockerClientStarter.java similarity index 65% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/DockerClientConfig.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/docker/DockerClientStarter.java index ea20e81a..fae3840a 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/DockerClientConfig.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/container/docker/DockerClientStarter.java @@ -14,20 +14,26 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client.config; +package io.agentscope.runtime.sandbox.manager.client.container.docker; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerManagerType; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerClientType; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class DockerClientConfig extends BaseClientConfig { +public class DockerClientStarter extends BaseClientStarter { + Logger logger = LoggerFactory.getLogger(DockerClientStarter.class); private String host; private int port; private String certPath; - private DockerClientConfig() { - super(ContainerManagerType.DOCKER); + + private DockerClientStarter() { + super(ContainerClientType.DOCKER); } - private DockerClientConfig(String host, int port, String certPath) { - super(ContainerManagerType.DOCKER); + private DockerClientStarter(String host, int port, String certPath) { + super(ContainerClientType.DOCKER); this.host = host; this.port = port; this.certPath = certPath; @@ -61,10 +67,19 @@ public void setCertPath(String certPath) { this.certPath = certPath; } + @Override + public DockerClient startClient(PortManager portManager) { + DockerClient dockerClient = new DockerClient(this, portManager); + dockerClient.connect(); + logger.info("Docker client created"); + return dockerClient; + } + public static class Builder { private String host = "localhost"; private int port = 2375; private String certPath; + private Builder() { } @@ -83,8 +98,8 @@ public Builder certPath(String certPath) { return this; } - public DockerClientConfig build() { - return new DockerClientConfig(host, port, certPath); + public DockerClientStarter build() { + return new DockerClientStarter(host, port, certPath); } } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/SandboxClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/SandboxClient.java similarity index 94% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/util/SandboxClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/SandboxClient.java index e3050db9..fffc7eb9 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/SandboxClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/SandboxClient.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.util; +package io.agentscope.runtime.sandbox.manager.client.sandbox; import java.util.Map; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/SandboxHttpClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/SandboxHttpClient.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/util/SandboxHttpClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/SandboxHttpClient.java index c35265e3..2b02a072 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/SandboxHttpClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/SandboxHttpClient.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.util; +package io.agentscope.runtime.sandbox.manager.client.sandbox; import com.fasterxml.jackson.databind.ObjectMapper; import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/TrainingSandboxClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/TrainingSandboxClient.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/util/TrainingSandboxClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/TrainingSandboxClient.java index 028cded9..c94b2dc9 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/TrainingSandboxClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/client/sandbox/TrainingSandboxClient.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.util; +package io.agentscope.runtime.sandbox.manager.client.sandbox; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -428,4 +428,3 @@ public Map listTools(String toolType, Map argume public void close() throws IOException { } } - diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/FileSystemConfig.java similarity index 71% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemConfig.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/FileSystemConfig.java index ff2eb04d..0e5d935f 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemConfig.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/FileSystemConfig.java @@ -13,22 +13,53 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.model.fs; + +package io.agentscope.runtime.sandbox.manager.fs; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.oss.OssConfig; +import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemType; import java.util.HashMap; import java.util.Map; -public class FileSystemConfig { - private FileSystemType fileSystemType; +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "fileSystemType" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = LocalFileSystemConfig.class, name = "LOCAL"), + @JsonSubTypes.Type(value = OssConfig.class, name = "OSS") +}) +public abstract class FileSystemConfig { + private final FileSystemType fileSystemType; private Map readonlyMounts; private String storageFolderPath; private String mountDir; private Map nonCopyMount; + protected FileSystemConfig( + FileSystemType fileSystemType, + Map readonlyMounts, + String storageFolderPath, + String mountDir, + Map nonCopyMount) { + this.fileSystemType = fileSystemType; + this.readonlyMounts = readonlyMounts; + this.storageFolderPath = storageFolderPath; + this.mountDir = mountDir; + this.nonCopyMount = nonCopyMount; + } + protected FileSystemConfig(FileSystemType fileSystemType) { this.fileSystemType = fileSystemType; } + public abstract StorageManager createStorageManager(); + protected FileSystemConfig(Builder builder) { this.fileSystemType = builder.fileSystemType; this.readonlyMounts = builder.readonlyMounts; diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/StorageManager.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/StorageManager.java new file mode 100644 index 00000000..9854dbf3 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/StorageManager.java @@ -0,0 +1,50 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.agentscope.runtime.sandbox.manager.fs; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Storage Manager responsible for handling file downloads from local and cloud storage + */ +public abstract class StorageManager{ + private static final Logger logger = LoggerFactory.getLogger(StorageManager.class); + + protected final FileSystemConfig fileSystemConfig; + + public StorageManager(FileSystemConfig fileSystemConfig) { + this.fileSystemConfig = fileSystemConfig; + } + + /** + * Download folder from storage to local directory + * + * @param storagePath storage path + * @param localDir local target directory + * @return whether download succeeded + */ + public abstract boolean downloadFolder(String storagePath, String localDir); + + /** + * Upload local folder to storage + * + * @param localDir local source directory + * @param storagePath storage path + * @return whether upload succeeded + */ + public abstract boolean uploadFolder(String localDir, String storagePath); +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/LocalFileSystemConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/local/LocalFileSystemConfig.java similarity index 56% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/LocalFileSystemConfig.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/local/LocalFileSystemConfig.java index e90e4d97..328b7500 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/LocalFileSystemConfig.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/local/LocalFileSystemConfig.java @@ -13,10 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.model.fs; +package io.agentscope.runtime.sandbox.manager.fs.local; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.StorageManager; +import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemType; + +import java.util.Map; public class LocalFileSystemConfig extends FileSystemConfig { + @JsonCreator + public LocalFileSystemConfig( + @JsonProperty("readonlyMounts") Map readonlyMounts, + @JsonProperty("storageFolderPath") String storageFolderPath, + @JsonProperty("mountDir") String mountDir, + @JsonProperty("nonCopyMount") Map nonCopyMount + ){ + super(FileSystemType.LOCAL, readonlyMounts, storageFolderPath, mountDir, nonCopyMount); + } + private LocalFileSystemConfig(Builder builder) { super(builder); } @@ -25,6 +43,11 @@ public static Builder builder() { return new Builder(); } + @Override + public StorageManager createStorageManager() { + return new LocalStorageManager(this); + } + public static class Builder extends FileSystemConfig.Builder { public Builder() { diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/local/LocalStorageManager.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/local/LocalStorageManager.java new file mode 100644 index 00000000..c3237700 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/local/LocalStorageManager.java @@ -0,0 +1,130 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.manager.fs.local; + +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.StorageManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +public class LocalStorageManager extends StorageManager { + Logger logger = LoggerFactory.getLogger(LocalStorageManager.class); + + public LocalStorageManager(FileSystemConfig fileSystemConfig) { + super(fileSystemConfig); + } + + @Override + public boolean downloadFolder(String storagePath, String localDir) { + if (storagePath == null || storagePath.isEmpty()) { + logger.warn("Storage path is empty, skipping download"); + return false; + } + + if (localDir == null || localDir.isEmpty()) { + logger.warn("Local directory is empty, skipping download"); + return false; + } + + try { + return copyLocalFolder(storagePath, localDir); + } catch (Exception e) { + logger.error("Failed to download folder from {} to {}: {}", storagePath, localDir, e.getMessage()); + return false; + } + } + + private boolean copyLocalFolder(String sourcePath, String targetPath) { + try { + Path source = Paths.get(sourcePath); + Path target = Paths.get(targetPath); + + if (!Files.exists(source)) { + logger.warn("Source path does not exist: {}", sourcePath); + return false; + } + + // Ensure target directory exists + if (!Files.exists(target)) { + Files.createDirectories(target); + } + + // If source is directory, copy recursively + if (Files.isDirectory(source)) { + Files.walk(source).forEach(srcPath -> { + try { + Path destPath = target.resolve(source.relativize(srcPath)); + if (Files.isDirectory(srcPath)) { + if (!Files.exists(destPath)) { + Files.createDirectories(destPath); + } + } else { + Files.copy(srcPath, destPath, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + logger.warn("Failed to copy {}: {}", srcPath, e.getMessage()); + } + }); + } else { + // If source is file, copy directly + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } + + logger.info("Copied folder from {} to {}", sourcePath, targetPath); + return true; + + } catch (Exception e) { + logger.error("Failed to copy local folder: {}", e.getMessage()); + return false; + } + } + + + @Override + public boolean uploadFolder(String localDir, String storagePath) { + if (localDir == null || localDir.isEmpty()) { + logger.warn("Local directory is empty, skipping upload"); + return false; + } + + if (storagePath == null || storagePath.isEmpty()) { + logger.warn("Storage path is empty, skipping upload"); + return false; + } + + // Check if local directory exists + File localDirectory = new File(localDir); + if (!localDirectory.exists()) { + logger.warn("Local directory does not exist: {}", localDir); + return false; + } + + try { + return copyLocalFolder(localDir, storagePath); + } catch (Exception e) { + logger.error("Failed to upload folder from {} to {}: {}", localDir, storagePath, e.getMessage()); + return false; + } + } +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/OssConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/oss/OssConfig.java similarity index 61% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/OssConfig.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/oss/OssConfig.java index 4c364f33..0c2f5ab6 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/OssConfig.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/oss/OssConfig.java @@ -13,13 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.model.fs; +package io.agentscope.runtime.sandbox.manager.fs.oss; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.agentscope.runtime.sandbox.manager.fs.FileSystemConfig; +import io.agentscope.runtime.sandbox.manager.fs.StorageManager; +import io.agentscope.runtime.sandbox.manager.model.fs.FileSystemType; + +import java.util.Map; public class OssConfig extends FileSystemConfig { - private String ossEndpoint; - private String ossAccessKeyId; - private String ossAccessKeySecret; - private String ossBucketName; + private final String ossEndpoint; + private final String ossAccessKeyId; + private final String ossAccessKeySecret; + private final String ossBucketName; + + @JsonCreator + public OssConfig( + @JsonProperty("readonlyMounts") Map readonlyMounts, + @JsonProperty("storageFolderPath") String storageFolderPath, + @JsonProperty("mountDir") String mountDir, + @JsonProperty("nonCopyMount") Map nonCopyMount, + @JsonProperty("ossEndpoint") String ossEndpoint, + @JsonProperty("ossAccessKeyId") String ossAccessKeyId, + @JsonProperty("ossAccessKeySecret") String ossAccessKeySecret, + @JsonProperty("ossBucketName") String ossBucketName + ){ + super(FileSystemType.OSS, readonlyMounts, storageFolderPath, mountDir, nonCopyMount); + this.ossEndpoint = ossEndpoint; + this.ossAccessKeyId = ossAccessKeyId; + this.ossAccessKeySecret = ossAccessKeySecret; + this.ossBucketName = ossBucketName; + } private OssConfig(Builder builder) { super(builder); @@ -49,6 +75,11 @@ public static Builder builder() { return new Builder(); } + @Override + public StorageManager createStorageManager() { + return new OssStorageManager(this); + } + public static class Builder extends FileSystemConfig.Builder { private String ossEndpoint = "http://oss-cn-hangzhou.aliyuncs.com"; private String ossAccessKeyId; diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/oss/OssStorageManager.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/oss/OssStorageManager.java new file mode 100644 index 00000000..673033c5 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/fs/oss/OssStorageManager.java @@ -0,0 +1,158 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.manager.fs.oss; + +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.model.ListObjectsRequest; +import com.aliyun.oss.model.OSSObject; +import com.aliyun.oss.model.OSSObjectSummary; +import com.aliyun.oss.model.ObjectListing; +import io.agentscope.runtime.sandbox.manager.fs.StorageManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +public class OssStorageManager extends StorageManager { + Logger logger = LoggerFactory.getLogger(OssStorageManager.class); + private final OSS ossClient; + + public OssStorageManager(OssConfig ossStarter) { + super(ossStarter); + this.ossClient = new OSSClientBuilder().build( + ossStarter.getOssEndpoint(), + ossStarter.getOssAccessKeyId(), + ossStarter.getOssAccessKeySecret() + ); + logger.info("OSS client initialized with endpoint: {}", ossStarter.getOssEndpoint()); + } + + @Override + public boolean downloadFolder(String storagePath, String localDir) { + if (storagePath == null || storagePath.isEmpty()) { + logger.warn("Storage path is empty, skipping download"); + return false; + } + + if (localDir == null || localDir.isEmpty()) { + logger.warn("Local directory is empty, skipping download"); + return false; + } + + try { + return downloadFromOss(storagePath, localDir); + } catch (Exception e) { + logger.error("Failed to download folder from {} to {}: {}", storagePath, localDir, e.getMessage()); + return false; + } + } + + /** + * Download folder from OSS + * + * @param ossPrefix OSS object prefix + * @param localDir local target directory + * @return whether download succeeded + */ + private boolean downloadFromOss(String ossPrefix, String localDir) { + if (ossClient == null || !(fileSystemConfig instanceof OssConfig ossStarter)) { + logger.error("OSS client not initialized"); + return false; + } + + try { + // Ensure local directory exists + File localDirectory = new File(localDir); + if (!localDirectory.exists()) { + boolean ignored = localDirectory.mkdirs(); + } + + String bucketName = ossStarter.getOssBucketName(); + + // Ensure prefix ends with / + String prefix = ossPrefix.endsWith("/") ? ossPrefix : ossPrefix + "/"; + + logger.info("Downloading from OSS bucket: {}, prefix: {} to {}", bucketName, prefix, localDir); + + // List all objects + ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName) + .withPrefix(prefix) + .withMaxKeys(1000); + + ObjectListing objectListing; + int downloadedCount = 0; + + do { + objectListing = ossClient.listObjects(listObjectsRequest); + + for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) { + String objectKey = objectSummary.getKey(); + + // Skip directory marker objects + if (objectKey.endsWith("/")) { + continue; + } + + // Calculate relative path + String relativePath = objectKey.substring(prefix.length()); + File localFile = new File(localDir, relativePath); + + // Ensure parent directory exists + File parentDir = localFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + boolean ignores = parentDir.mkdirs(); + } + + // Download file + try { + OSSObject ossObject = ossClient.getObject(bucketName, objectKey); + try (InputStream inputStream = ossObject.getObjectContent(); + FileOutputStream outputStream = new FileOutputStream(localFile)) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + downloadedCount++; + logger.info("Downloaded: {} to {}", objectKey, localFile.getAbsolutePath()); + } catch (Exception e) { + logger.warn("Failed to download {}: {}", objectKey, e.getMessage()); + } + } + + listObjectsRequest.setMarker(objectListing.getNextMarker()); + } while (objectListing.isTruncated()); + + logger.info("Downloaded {} files from OSS", downloadedCount); + return true; + + } catch (Exception e) { + logger.error("Failed to download from OSS: {}", e.getMessage()); + return false; + } + } + + + @Override + public boolean uploadFolder(String localDir, String storagePath) { + return false; + } +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerManagerType.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerClientType.java similarity index 73% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerManagerType.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerClientType.java index 1b341bbf..fe05b73e 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerManagerType.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerClientType.java @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.agentscope.runtime.sandbox.manager.model.container; -/** - * Container management type enumeration - */ -public enum ContainerManagerType { - DOCKER("docker"), KUBERNETES("kubernetes"), CLOUD("cloud"), AGENTRUN("agentrun"), FC("fc"); +public enum ContainerClientType { + DOCKER("docker"), KUBERNETES("kubernetes"), AGENTRUN("agentrun"), FC("fc"); private final String value; - ContainerManagerType(String value) { + ContainerClientType(String value) { this.value = value; } @@ -31,8 +29,8 @@ public String getValue() { return value; } - public static ContainerManagerType fromString(String value) { - for (ContainerManagerType type : ContainerManagerType.values()) { + public static ContainerClientType fromString(String value) { + for (ContainerClientType type : ContainerClientType.values()) { if (type.value.equalsIgnoreCase(value)) { return type; } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerModel.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerModel.java similarity index 97% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerModel.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerModel.java index ca819284..2e245e76 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerModel.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/ContainerModel.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.agentscope.runtime.sandbox.manager.model.container; import java.util.List; @@ -45,7 +46,7 @@ public static ContainerModel fromMap(Map map) { if (map == null) { return null; } - + ContainerModel model = new ContainerModel(); model.setSessionId((String) map.get("sessionId")); model.setContainerId((String) map.get("containerId")); @@ -60,19 +61,18 @@ public static ContainerModel fromMap(Map map) { model.setRuntimeToken((String) map.get("runtimeToken")); model.setVersion((String) map.get("version")); model.setAuthToken((String) map.get("authToken")); - + // Handle ports array Object portsObj = map.get("ports"); - if (portsObj instanceof List) { - List portsList = (List) portsObj; + if (portsObj instanceof List portsList) { String[] ports = portsList.stream() - .map(Object::toString) - .toArray(String[]::new); + .map(Object::toString) + .toArray(String[]::new); model.setPorts(ports); } else if (portsObj instanceof String[]) { model.setPorts((String[]) portsObj); } - + return model; } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/PortRange.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/PortRange.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/PortRange.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/PortRange.java index 9bf8512b..20611075 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/PortRange.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/PortRange.java @@ -35,4 +35,4 @@ public int getStart() { public int getEnd() { return end; } -} \ No newline at end of file +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxType.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxKey.java similarity index 58% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxType.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxKey.java index 4d13e0a3..8add8ea8 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxType.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxKey.java @@ -13,31 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.model.container; -/** - * Sandbox type enumeration - */ -public enum SandboxType { - BASE("base"), - BROWSER("browser"), - FILESYSTEM("filesystem"), - TRAINING("training"), - APPWORLD("appworld"), - BFCL("bfcl"), - WEBSHOP("webshop"), - GUI("gui"), - MOBILE("mobile"), - CUSTOM("custom"), - AGENTBAY("agentbay"); +package io.agentscope.runtime.sandbox.manager.model.container; - private final String typeName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - SandboxType(String typeName) { - this.typeName = typeName; - } +public record SandboxKey(String userID, String sessionID, String sandboxType) { + private static final Logger logger = LoggerFactory.getLogger(SandboxKey.class); - public String getTypeName() { - return typeName; + @Override + public String toString() { + return "SandboxKey{" + "userID='" + userID + '\'' + ", sessionID='" + sessionID + '\'' + ", sandboxType=" + sandboxType + '}'; } -} \ No newline at end of file +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemType.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemType.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemType.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemType.java index 3d78e3e4..bca0b997 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemType.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/FileSystemType.java @@ -38,4 +38,4 @@ public static FileSystemType fromString(String value) { } throw new IllegalArgumentException("Unknown file system type: " + value); } -} \ No newline at end of file +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/VolumeBinding.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/VolumeBinding.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/VolumeBinding.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/VolumeBinding.java index 218fea69..93d1f10d 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/VolumeBinding.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/fs/VolumeBinding.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.agentscope.runtime.sandbox.manager.model.fs; public class VolumeBinding { diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxConfig.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/sandbox/SandboxConfig.java similarity index 95% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxConfig.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/sandbox/SandboxConfig.java index 7236b803..9725afbe 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/SandboxConfig.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/model/sandbox/SandboxConfig.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.model.container; +package io.agentscope.runtime.sandbox.manager.model.sandbox; import java.util.HashMap; import java.util.Map; @@ -21,7 +21,7 @@ public class SandboxConfig { private final String imageName; - private final SandboxType sandboxType; + private final String sandboxType; private final Map resourceLimits; private final String securityLevel; private final int timeout; @@ -59,7 +59,7 @@ public String getImageName() { return imageName; } - public SandboxType getSandboxType() { + public String getSandboxType() { return sandboxType; } @@ -110,7 +110,7 @@ public String toString() { */ public static class Builder { private String imageName; - private SandboxType sandboxType; + private String sandboxType; private Map resourceLimits; private String securityLevel = "medium"; private int timeout = 300; @@ -123,7 +123,7 @@ public Builder imageName(String imageName) { return this; } - public Builder sandboxType(SandboxType sandboxType) { + public Builder sandboxType(String sandboxType) { this.sandboxType = sandboxType; return this; } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/BuiltInSandboxProvider.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/BuiltInSandboxProvider.java similarity index 89% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/BuiltInSandboxProvider.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/BuiltInSandboxProvider.java index 02f0c986..7777a727 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/BuiltInSandboxProvider.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/BuiltInSandboxProvider.java @@ -17,23 +17,22 @@ import io.agentscope.runtime.sandbox.box.*; -import java.util.Arrays; import java.util.Collection; +import java.util.List; public class BuiltInSandboxProvider implements SandboxProvider { @Override public Collection> getSandboxClasses() { - return Arrays.asList( + return List.of( BaseSandbox.class, FilesystemSandbox.class, BrowserSandbox.class, - BFCLSandbox.class, - WebShopSandbox.class, APPWorldSandbox.class, + BFCLSandbox.class, GuiSandbox.class, MobileSandbox.class, - AgentBaySandbox.class + WebShopSandbox.class ); } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/RegisterSandbox.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/RegisterSandbox.java similarity index 90% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/RegisterSandbox.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/RegisterSandbox.java index 8b07909d..6f17edac 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/RegisterSandbox.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/RegisterSandbox.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.registry; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +package io.agentscope.runtime.sandbox.manager.registry; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -27,7 +26,7 @@ public @interface RegisterSandbox { String imageName(); - SandboxType sandboxType() default SandboxType.BASE; + String sandboxType() default "base"; String customType() default ""; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxAnnotationProcessor.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxAnnotationProcessor.java similarity index 64% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxAnnotationProcessor.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxAnnotationProcessor.java index a8b03707..db4560b7 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxAnnotationProcessor.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxAnnotationProcessor.java @@ -15,9 +15,7 @@ */ package io.agentscope.runtime.sandbox.manager.registry; -import io.agentscope.runtime.sandbox.box.*; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxConfig; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.model.sandbox.SandboxConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,24 +34,23 @@ public static void processClass(Class clazz) { if (clazz == null) { return; } - + RegisterSandbox annotation = clazz.getAnnotation(RegisterSandbox.class); if (annotation == null) { return; } - + try { String imageName = annotation.imageName(); - String customType = annotation.customType(); - SandboxType sandboxType = annotation.sandboxType(); + String sandboxType = annotation.sandboxType(); String securityLevel = annotation.securityLevel(); int timeout = annotation.timeout(); String description = annotation.description(); - + Map environment = parseKeyValueArray(annotation.environment()); Map resourceLimits = parseResourceLimits(annotation.resourceLimits()); Map runtimeConfig = parseKeyValueArrayAsObject(annotation.runtimeConfig()); - + SandboxConfig config = new SandboxConfig.Builder() .imageName(imageName) .sandboxType(sandboxType) @@ -64,104 +61,18 @@ public static void processClass(Class clazz) { .resourceLimits(resourceLimits) .runtimeConfig(runtimeConfig) .build(); - - if (customType != null && !customType.isEmpty()) { - SandboxRegistryService.registerCustomType( - customType, - imageName, - resourceLimits, - securityLevel, - timeout, - description, - environment, - runtimeConfig - ); - logger.info("Registered custom sandbox via annotation: type={}, class={}, image={}", - customType, clazz.getSimpleName(), imageName); - } else { - SandboxRegistryService.register(clazz, config); - logger.info("Registered sandbox via annotation: type={}, class={}, image={}", - sandboxType, clazz.getSimpleName(), imageName); - } - + + SandboxRegistryService.register(clazz, config); + logger.info("Registered sandbox via annotation: type={}, class={}, image={}", + sandboxType, clazz.getSimpleName(), imageName); + } catch (Exception e) { logger.error("Failed to process @RegisterSandbox annotation on class {}: {}", - clazz.getName(), e.getMessage()); + clazz.getName(), e.getMessage()); throw new RuntimeException("Failed to register sandbox: " + clazz.getName(), e); } } - - /** - * Scan and process all classes in the specified package - * - * @param packageName Package name - */ - public static void scanAndRegisterPackage(String packageName) { - logger.info("Scanning package for @RegisterSandbox annotations: {}", packageName); - logger.warn("Package scanning not fully implemented. " + - "Please call processClass() manually for each sandbox class " + - "or use a classpath scanner like Reflections library."); - } - - /** - * Manually register all known Sandbox classes - * This method should be called at application startup - */ - public static void registerAllKnownSandboxes() { - logger.info("Registering all known sandbox classes with @RegisterSandbox annotation"); - - try { - Class baseSandboxClass = Class.forName(BaseSandbox.class.getName()); - processClass(baseSandboxClass); - } catch (ClassNotFoundException e) { - logger.info("BaseSandbox class not found, skipping: {}", e.getMessage()); - } - - try { - Class filesystemSandboxClass = Class.forName(FilesystemSandbox.class.getName()); - processClass(filesystemSandboxClass); - } catch (ClassNotFoundException e) { - logger.info("FilesystemSandbox class not found, skipping: {}", e.getMessage()); - } - - try { - Class browserSandboxClass = Class.forName(BrowserSandbox.class.getName()); - processClass(browserSandboxClass); - } catch (ClassNotFoundException e) { - logger.info("BrowserSandbox class not found, skipping: {}", e.getMessage()); - } - - try { - Class dummySandboxClass = Class.forName(DummySandbox.class.getName()); - processClass(dummySandboxClass); - } catch (ClassNotFoundException e) { - logger.info("DummySandbox class not found, skipping: {}", e.getMessage()); - } - - try { - Class bfclSandboxClass = Class.forName(BFCLSandbox.class.getName()); - processClass(bfclSandboxClass); - } catch (ClassNotFoundException e) { - logger.info("BFCLSandbox class not found, skipping: {}", e.getMessage()); - } - - try { - Class appworldSandboxClass = Class.forName(APPWorldSandbox.class.getName()); - processClass(appworldSandboxClass); - } catch (ClassNotFoundException e) { - logger.info("APPWorldSandbox class not found, skipping: {}", e.getMessage()); - } - - try { - Class webshopSandboxClass = Class.forName(WebShopSandbox.class.getName()); - processClass(webshopSandboxClass); - } catch (ClassNotFoundException e) { - logger.info("WebShopSandbox class not found, skipping: {}", e.getMessage()); - } - - logger.info("Finished registering sandbox classes"); - } - + /** * Parse key-value array to Map * Format: "key1=value1", "key2=value2" @@ -172,12 +83,12 @@ private static Map parseKeyValueArray(String[] array) { if (array == null) { return result; } - + for (String item : array) { if (item == null || item.trim().isEmpty()) { continue; } - + int equalIndex = item.indexOf('='); if (equalIndex > 0) { String key = item.substring(0, equalIndex).trim(); @@ -188,14 +99,14 @@ private static Map parseKeyValueArray(String[] array) { logger.warn("Invalid key-value pair format: {}", item); } } - + return result; } - + /** * Resolve environment variable placeholders in a string * Supports syntax: ${VAR_NAME} or ${VAR_NAME:default_value} - * + * * @param value String that may contain placeholders * @return String with placeholders replaced by actual environment variable values */ @@ -203,29 +114,29 @@ private static String resolveEnvironmentVariables(String value) { if (value == null || !value.contains("${")) { return value; } - + StringBuilder result = new StringBuilder(); int startIndex = 0; - + while (startIndex < value.length()) { int placeholderStart = value.indexOf("${", startIndex); if (placeholderStart == -1) { result.append(value.substring(startIndex)); break; } - + int placeholderEnd = value.indexOf("}", placeholderStart); if (placeholderEnd == -1) { result.append(value.substring(startIndex)); break; } - + result.append(value.substring(startIndex, placeholderStart)); - + String placeholder = value.substring(placeholderStart + 2, placeholderEnd); String envVarName; String defaultValue = ""; - + int colonIndex = placeholder.indexOf(':'); if (colonIndex > 0) { envVarName = placeholder.substring(0, colonIndex).trim(); @@ -233,7 +144,7 @@ private static String resolveEnvironmentVariables(String value) { } else { envVarName = placeholder.trim(); } - + String envValue = System.getenv(envVarName); if (envValue != null) { result.append(envValue); @@ -241,13 +152,13 @@ private static String resolveEnvironmentVariables(String value) { result.append(defaultValue); logger.info("Environment variable '{}' not found, using default: '{}'", envVarName, defaultValue); } - + startIndex = placeholderEnd + 1; } - + return result.toString(); } - + /** * Parse key-value array to Map * Format: "key1=value1", "key2=value2" @@ -257,12 +168,12 @@ private static Map parseKeyValueArrayAsObject(String[] array) { if (array == null) { return result; } - + for (String item : array) { if (item == null || item.trim().isEmpty()) { continue; } - + int equalIndex = item.indexOf('='); if (equalIndex > 0) { String key = item.substring(0, equalIndex).trim(); @@ -273,10 +184,10 @@ private static Map parseKeyValueArrayAsObject(String[] array) { logger.warn("Invalid key-value pair format: {}", item); } } - + return result; } - + /** * Parse resource limits configuration * Supported format: "memory=1g", "cpu=2.0" @@ -286,17 +197,17 @@ private static Map parseResourceLimits(String[] array) { if (array == null) { return result; } - + for (String item : array) { if (item == null || item.trim().isEmpty()) { continue; } - + int equalIndex = item.indexOf('='); if (equalIndex > 0) { String key = item.substring(0, equalIndex).trim(); String value = item.substring(equalIndex + 1).trim(); - + if ("memory".equalsIgnoreCase(key)) { result.put("memory", value); } else if ("cpu".equalsIgnoreCase(key)) { @@ -312,10 +223,10 @@ private static Map parseResourceLimits(String[] array) { } } } - + return result; } - + /** * Try to parse string value to appropriate type (String, Integer, Double, Boolean) */ @@ -323,23 +234,23 @@ private static Object parseValue(String value) { if (value == null) { return null; } - + if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) { return Boolean.parseBoolean(value); } - + try { return Integer.parseInt(value); } catch (NumberFormatException e) { // Not an integer, continue trying } - + try { return Double.parseDouble(value); } catch (NumberFormatException e) { // Not a double, return string } - + return value; } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxProvider.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxProvider.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxProvider.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxProvider.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryInitializer.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryInitializer.java similarity index 89% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryInitializer.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryInitializer.java index 65490d67..08f90c7e 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryInitializer.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryInitializer.java @@ -58,6 +58,7 @@ private static void registerViaSpi() { Set> registered = new LinkedHashSet<>(); ServiceLoader loader = ServiceLoader.load(SandboxProvider.class); + for (SandboxProvider provider : loader) { try { Collection> classes = provider.getSandboxClasses(); @@ -90,22 +91,4 @@ private static void registerViaSpi() { .collect(Collectors.joining(", ")); logger.info("Registered sandboxes via SPI: {}", summary); } - - - /** - * Reset initialization state (mainly for testing) - */ - public static synchronized void reset() { - initialized = false; - logger.info("SandboxRegistryService initialization state reset"); - } - - /** - * Check if already initialized - * - * @return true if initialized, false otherwise - */ - public static boolean isInitialized() { - return initialized; - } } diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryService.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryService.java new file mode 100644 index 00000000..b22d3306 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/registry/SandboxRegistryService.java @@ -0,0 +1,84 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.agentscope.runtime.sandbox.manager.registry; + +import io.agentscope.runtime.sandbox.manager.model.sandbox.SandboxConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Sandbox registry for managing sandbox configurations + */ +public class SandboxRegistryService { + private static final Logger logger = LoggerFactory.getLogger(SandboxRegistryService.class); + private static final Map typeConfigRegistry = new ConcurrentHashMap<>(); + + static { + try { + Class.forName(SandboxRegistryInitializer.class.getName()); + logger.info("SandboxRegistryInitializer loaded and executed"); + } catch (ClassNotFoundException e) { + logger.warn("SandboxRegistryInitializer not found, annotation-based registration disabled"); + } + } + + /** + * Register a sandbox configuration for a specific class and type + * + * @param targetClass The class to register + * @param config The sandbox configuration + */ + public static void register(Class targetClass, SandboxConfig config) { + if (targetClass == null) { + throw new IllegalArgumentException("Target class cannot be null"); + } + if (config == null) { + throw new IllegalArgumentException("Sandbox configuration cannot be null"); + } + + String sandboxType = config.getSandboxType(); + + typeConfigRegistry.put(sandboxType, config); + + logger.info("Registered sandbox: type={}, class={}, image={}", sandboxType, targetClass.getSimpleName(), config.getImageName()); + } + + + /** + * Get sandbox configuration by type + * + * @param sandboxType The sandbox type + * @return Optional containing the configuration if found + */ + public static Optional getConfigByType(String sandboxType) { + return Optional.ofNullable(typeConfigRegistry.get(sandboxType)); + } + + /** + * Get Docker image name by sandbox type + * + * @param sandboxType The sandbox type + * @return Optional containing the image name if found + */ + public static Optional getImageByType(String sandboxType) { + return getConfigByType(sandboxType).map(SandboxConfig::getImageName); + } +} + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteHttpClient.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteHttpClient.java similarity index 98% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteHttpClient.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteHttpClient.java index 4b537d52..9c5400be 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteHttpClient.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteHttpClient.java @@ -29,8 +29,8 @@ import java.util.Map; /** - * HTTP client for making remote calls to SandboxManager server. - * This client handles the communication with the remote SandboxManager service. + * HTTP client for making remote calls to SandboxService server. + * This client handles the communication with the remote SandboxService service. */ public class RemoteHttpClient { @@ -58,6 +58,7 @@ public RemoteHttpClient(String baseUrl, String bearerToken) { */ public Object makeRequest(RequestMethod method, String endpoint, Map data, String successKey) { String url = baseUrl + (endpoint.startsWith("/") ? endpoint : "/" + endpoint); + System.out.println(data); logger.info("Making {} request to: {}", method, url); diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteWrapper.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteWrapper.java similarity index 96% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteWrapper.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteWrapper.java index 0ec102a7..fde2e1c4 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteWrapper.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RemoteWrapper.java @@ -22,7 +22,7 @@ /** * Annotation to mark methods that can be executed both locally and remotely. - * When a SandboxManager is configured with a remote base URL, methods annotated + * When a SandboxService is configured with a remote base URL, methods annotated * with @RemoteWrapper will automatically make HTTP requests to the remote server */ @Target(ElementType.METHOD) diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RequestMethod.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RequestMethod.java similarity index 96% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RequestMethod.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RequestMethod.java index bb3f1f20..1fc503a5 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RequestMethod.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/remote/RequestMethod.java @@ -21,11 +21,8 @@ public enum RequestMethod { GET, POST, - PUT, DELETE, - PATCH, HEAD, - OPTIONS } diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/InMemorySandboxMap.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/InMemorySandboxMap.java new file mode 100644 index 00000000..76738ada --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/InMemorySandboxMap.java @@ -0,0 +1,100 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.manager.utils; + +import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import io.agentscope.runtime.sandbox.manager.model.container.SandboxKey; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class InMemorySandboxMap implements SandboxMap{ + private final Map idContainerMap = new ConcurrentHashMap<>(); + private final Map keyIdMap = new ConcurrentHashMap<>(); + + @Override + public void addSandbox(SandboxKey sandboxKey, ContainerModel containerModel) { + if(sandboxKey == null || containerModel == null){ + return; + } + keyIdMap.put(sandboxKey, containerModel.getContainerId()); + idContainerMap.put(containerModel.getContainerId(), containerModel); + } + + @Override + public ContainerModel getSandbox(SandboxKey sandboxKey) { + if(!keyIdMap.containsKey(sandboxKey)){ + return null; + } + return idContainerMap.get(keyIdMap.get(sandboxKey)); + } + + @Override + public boolean removeSandbox(SandboxKey sandboxKey) { + if (sandboxKey == null) { + return false; + } + String containerId = keyIdMap.remove(sandboxKey); + if (containerId != null) { + idContainerMap.remove(containerId); + } + return true; + } + + @Override + public ContainerModel getSandbox(String containerId) { + if (containerId == null || containerId.isEmpty()) { + return null; + } + return idContainerMap.get(containerId); + } + + @Override + public void removeSandbox(String containerId) { + if (containerId == null || containerId.isEmpty()) { + return; + } + ContainerModel containerModel = idContainerMap.remove(containerId); + if (containerModel != null) { + keyIdMap.values().removeIf(id -> id.equals(containerId)); + } + } + + @Override + public Map getAllSandboxes() { + if (idContainerMap.isEmpty()) { + return Map.of(); + } + return idContainerMap; + } + + @Override + public boolean containSandbox(SandboxKey sandboxKey) { + if (sandboxKey == null) { + return false; + } + return keyIdMap.containsKey(sandboxKey); + } + + @Override + public boolean containSandbox(String containerId) { + if(containerId == null || containerId.isEmpty()){ + return false; + } + return idContainerMap.containsKey(containerId); + } +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/PortManager.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/PortManager.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/util/PortManager.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/PortManager.java index d4828994..9afee774 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/PortManager.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/PortManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.util; +package io.agentscope.runtime.sandbox.manager.utils; import io.agentscope.runtime.sandbox.manager.model.container.PortRange; import org.slf4j.Logger; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/RandomStringGenerator.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/RandomStringGenerator.java similarity index 96% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/util/RandomStringGenerator.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/RandomStringGenerator.java index cfdd27ef..2c2d78a6 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/RandomStringGenerator.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/RandomStringGenerator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.util; +package io.agentscope.runtime.sandbox.manager.utils; import java.security.SecureRandom; @@ -37,4 +37,4 @@ public static String generateRandomString(int length) { } return sb.toString(); } -} \ No newline at end of file +} diff --git a/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/SandboxMap.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/SandboxMap.java new file mode 100644 index 00000000..13a88eb9 --- /dev/null +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/manager/utils/SandboxMap.java @@ -0,0 +1,40 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.manager.utils; + +import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import io.agentscope.runtime.sandbox.manager.model.container.SandboxKey; + +import java.util.Map; + +public interface SandboxMap { + void addSandbox(SandboxKey sandboxKey, ContainerModel containerModel); + + ContainerModel getSandbox(SandboxKey sandboxKey); + + boolean removeSandbox(SandboxKey sandboxKey); + + ContainerModel getSandbox(String containerId); + + void removeSandbox(String containerId); + + Map getAllSandboxes(); + + boolean containSandbox(SandboxKey sandboxKey); + + boolean containSandbox(String containerId); +} diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/MCPTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/MCPTool.java similarity index 90% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/MCPTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/MCPTool.java index b995cef3..aac08b61 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/MCPTool.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/MCPTool.java @@ -17,8 +17,7 @@ import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +29,11 @@ public class MCPTool extends SandboxTool { private Map serverConfigs; private final String mcpToolName; - private final SandboxType sandboxType; + private final String sandboxType; public MCPTool(String name, String toolType, String description, Map schema, Map serverConfigs, - SandboxType sandboxType) { + String sandboxType) { super(name, toolType, description); this.mcpToolName = name; this.schema = schema; @@ -44,8 +43,8 @@ public MCPTool(String name, String toolType, String description, public MCPTool(String name, String toolType, String description, Map schema, Map serverConfigs, - SandboxType sandboxType, SandboxManager sandboxManager) { - super(name, toolType, description, sandboxManager); + String sandboxType, SandboxService sandboxService) { + super(name, toolType, description, sandboxService); this.mcpToolName = name; this.schema = schema; this.serverConfigs = serverConfigs; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/McpConfigConverter.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/McpConfigConverter.java similarity index 87% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/McpConfigConverter.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/McpConfigConverter.java index c3212f83..6bfcccfd 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/McpConfigConverter.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/McpConfigConverter.java @@ -16,22 +16,16 @@ package io.agentscope.runtime.sandbox.tools; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; +import io.agentscope.runtime.sandbox.manager.SandboxService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.*; + public class McpConfigConverter { private static final Logger logger = LoggerFactory.getLogger(McpConfigConverter.class); @@ -40,28 +34,28 @@ public class McpConfigConverter { private Map serverConfigs; private Set whitelist; private Set blacklist; - private SandboxManager sandboxManager; - private SandboxType sandboxType; + private SandboxService sandboxService; + private final String sandboxType; private Sandbox sandbox = null; - public McpConfigConverter(Map serverConfigs, SandboxType sandboxType, + public McpConfigConverter(Map serverConfigs, String sandboxType, Set whitelist, Set blacklist) { this(serverConfigs, sandboxType, whitelist, blacklist, null); } - public McpConfigConverter(Map serverConfigs, SandboxType sandboxType, + public McpConfigConverter(Map serverConfigs, String sandboxType, Set whitelist, Set blacklist, - SandboxManager sandboxManager) { - this(serverConfigs, sandboxType, whitelist, blacklist, sandboxManager, null); + SandboxService sandboxService) { + this(serverConfigs, sandboxType, whitelist, blacklist, sandboxService, null); } - public McpConfigConverter(Map serverConfigs, SandboxType sandboxType, + public McpConfigConverter(Map serverConfigs, String sandboxType, Set whitelist, Set blacklist, - SandboxManager sandboxManager, Sandbox sandbox) { + SandboxService sandboxService, Sandbox sandbox) { this.serverConfigs = serverConfigs; this.whitelist = whitelist != null ? whitelist : new HashSet<>(); this.blacklist = blacklist != null ? blacklist : new HashSet<>(); - this.sandboxManager = sandboxManager; + this.sandboxService = sandboxService; this.sandboxType = sandboxType; this.sandbox = sandbox; @@ -76,7 +70,7 @@ public McpConfigConverter bind(Sandbox sandbox) { sandboxType, new HashSet<>(this.whitelist), new HashSet<>(this.blacklist), - this.sandboxManager + this.sandboxService ); } @@ -104,19 +98,19 @@ public void setBlacklist(Set blacklist) { this.blacklist = blacklist; } - public SandboxManager getSandboxManager() { - return sandboxManager; + public SandboxService getSandboxService() { + return sandboxService; } - public void setSandboxManager(SandboxManager sandboxManager) { - this.sandboxManager = sandboxManager; + public void setSandboxService(SandboxService sandboxService) { + this.sandboxService = sandboxService; } public List toBuiltinTools() { Sandbox box = this.sandbox; List toolsToAdd = new ArrayList<>(); if(box == null){ - try(BaseSandbox baseSandbox = new BaseSandbox(sandboxManager, "", "")){ + try(BaseSandbox baseSandbox = new BaseSandbox(sandboxService, "", "")){ box = baseSandbox; toolsToAdd = processTools(box); } @@ -179,7 +173,7 @@ private List processTools(Sandbox box) { functionSchema, toolServerConfig, sandboxType, - sandboxManager + sandboxService ); toolsToAdd.add(mcpTool); @@ -229,10 +223,10 @@ public static Builder builder() { public static class Builder { private Map serverConfigs; - private SandboxType sandboxType = SandboxType.BASE; + private String sandboxType = "base"; private Set whitelist; private Set blacklist; - private SandboxManager sandboxManager; + private SandboxService sandboxService; public Builder serverConfigs(Map serverConfigs) { this.serverConfigs = serverConfigs; @@ -244,7 +238,7 @@ public Builder serverConfigs(String serverConfigsStr) { return this; } - public Builder sandboxType(SandboxType sandboxType) { + public Builder sandboxType(String sandboxType) { this.sandboxType = sandboxType; return this; } @@ -259,13 +253,13 @@ public Builder blacklist(Set blacklist) { return this; } - public Builder sandboxManager(SandboxManager sandboxManager) { - this.sandboxManager = sandboxManager; + public Builder sandboxService(SandboxService sandboxService) { + this.sandboxService = sandboxService; return this; } public McpConfigConverter build() { - return new McpConfigConverter(serverConfigs, sandboxType, whitelist, blacklist, sandboxManager); + return new McpConfigConverter(serverConfigs, sandboxType, whitelist, blacklist, sandboxService); } } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/SandboxTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/SandboxTool.java similarity index 79% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/SandboxTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/SandboxTool.java index 0f30fade..218a2009 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/SandboxTool.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/SandboxTool.java @@ -15,14 +15,15 @@ */ package io.agentscope.runtime.sandbox.tools; + import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; +import io.agentscope.runtime.sandbox.manager.SandboxService; import java.util.Map; public abstract class SandboxTool extends Tool { - protected SandboxManager sandboxManager; + protected SandboxService sandboxService; protected Sandbox sandbox; @@ -33,17 +34,17 @@ protected SandboxTool(String name, String toolType, String description) { } protected SandboxTool(String name, String toolType, String description, - SandboxManager sandboxManager) { + SandboxService sandboxService) { super(name, toolType, description); - this.sandboxManager = sandboxManager; + this.sandboxService = sandboxService; } - public SandboxManager getSandboxManager() { - return sandboxManager; + public SandboxService getSandboxService() { + return sandboxService; } - public void setSandboxManager(SandboxManager sandboxManager) { - this.sandboxManager = sandboxManager; + public void setSandboxService(SandboxService sandboxService) { + this.sandboxService = sandboxService; } public Sandbox getSandbox() { diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/Tool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/Tool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/Tool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/Tool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/base/BaseSandboxTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/BaseSandboxTool.java similarity index 77% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/base/BaseSandboxTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/BaseSandboxTool.java index 71c7d5ab..83be9a38 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/base/BaseSandboxTool.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/BaseSandboxTool.java @@ -34,21 +34,21 @@ import io.agentscope.runtime.sandbox.box.BaseSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.tools.SandboxTool; public abstract class BaseSandboxTool extends SandboxTool { - protected BaseSandboxTool(String name, String toolType, String description) { - super(name, toolType, description); - } + protected BaseSandboxTool(String name, String toolType, String description) { + super(name, toolType, description); + } - protected BaseSandboxTool(String name, String toolType, String description, SandboxManager sandboxManager) { - super(name, toolType, description, sandboxManager); - } + protected BaseSandboxTool(String name, String toolType, String description, SandboxService sandboxService) { + super(name, toolType, description, sandboxService); + } - @Override - public Class getSandboxClass() { - return BaseSandbox.class; - } + @Override + public Class getSandboxClass() { + return BaseSandbox.class; + } } diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunPythonTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunPythonTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunPythonTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunPythonTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunShellCommandTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunShellCommandTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunShellCommandTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/base/RunShellCommandTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/BrowserSandboxTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/BrowserSandboxTool.java similarity index 92% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/BrowserSandboxTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/BrowserSandboxTool.java index 7743c903..598a80da 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/BrowserSandboxTool.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/BrowserSandboxTool.java @@ -34,7 +34,7 @@ import io.agentscope.runtime.sandbox.box.BrowserSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.tools.SandboxTool; public abstract class BrowserSandboxTool extends SandboxTool { @@ -43,8 +43,8 @@ protected BrowserSandboxTool(String name, String toolType, String description) { super(name, toolType, description); } - protected BrowserSandboxTool(String name, String toolType, String description, SandboxManager sandboxManager) { - super(name, toolType, description, sandboxManager); + protected BrowserSandboxTool(String name, String toolType, String description, SandboxService sandboxService) { + super(name, toolType, description, sandboxService); } @Override diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ClickTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ClickTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ClickTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ClickTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/CloseTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/CloseTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/CloseTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/CloseTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ConsoleMessagesTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ConsoleMessagesTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ConsoleMessagesTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ConsoleMessagesTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/DragTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/DragTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/DragTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/DragTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/FileUploadTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/FileUploadTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/FileUploadTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/FileUploadTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HandleDialogTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HandleDialogTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HandleDialogTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HandleDialogTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HoverTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HoverTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HoverTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/HoverTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateBackTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateBackTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateBackTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateBackTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateForwardTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateForwardTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateForwardTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateForwardTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NavigateTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NetworkRequestsTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NetworkRequestsTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NetworkRequestsTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/NetworkRequestsTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PdfSaveTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PdfSaveTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PdfSaveTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PdfSaveTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PressKeyTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PressKeyTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PressKeyTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/PressKeyTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ResizeTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ResizeTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ResizeTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/ResizeTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SelectOptionTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SelectOptionTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SelectOptionTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SelectOptionTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SnapshotTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SnapshotTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SnapshotTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/SnapshotTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabCloseTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabCloseTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabCloseTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabCloseTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabListTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabListTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabListTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabListTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabNewTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabNewTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabNewTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabNewTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabSelectTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabSelectTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabSelectTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TabSelectTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TakeScreenshotTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TakeScreenshotTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TakeScreenshotTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TakeScreenshotTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TypeTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TypeTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TypeTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/TypeTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/WaitForTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/WaitForTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/WaitForTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/WaitForTool.java index af4825a7..781815ba 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/WaitForTool.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/browser/WaitForTool.java @@ -15,12 +15,12 @@ */ package io.agentscope.runtime.sandbox.tools.browser; -import com.fasterxml.jackson.annotation.JsonClassDescription; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; import io.agentscope.runtime.sandbox.box.BrowserSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; import io.agentscope.runtime.sandbox.tools.SandboxTool; +import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/CreateDirectoryTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/CreateDirectoryTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/CreateDirectoryTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/CreateDirectoryTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/DirectoryTreeTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/DirectoryTreeTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/DirectoryTreeTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/DirectoryTreeTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/EditFileTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/EditFileTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/EditFileTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/EditFileTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/FsSandboxTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/FsSandboxTool.java similarity index 92% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/FsSandboxTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/FsSandboxTool.java index b5b80caa..a55f3584 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/FsSandboxTool.java +++ b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/FsSandboxTool.java @@ -32,9 +32,10 @@ */ package io.agentscope.runtime.sandbox.tools.fs; + import io.agentscope.runtime.sandbox.box.FilesystemSandbox; import io.agentscope.runtime.sandbox.box.Sandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; +import io.agentscope.runtime.sandbox.manager.SandboxService; import io.agentscope.runtime.sandbox.tools.SandboxTool; public abstract class FsSandboxTool extends SandboxTool { @@ -43,8 +44,8 @@ protected FsSandboxTool(String name, String toolType, String description) { super(name, toolType, description); } - protected FsSandboxTool(String name, String toolType, String description, SandboxManager sandboxManager) { - super(name, toolType, description, sandboxManager); + protected FsSandboxTool(String name, String toolType, String description, SandboxService sandboxService) { + super(name, toolType, description, sandboxService); } @Override diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/GetFileInfoTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/GetFileInfoTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/GetFileInfoTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/GetFileInfoTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListAllowedDirectoriesTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListAllowedDirectoriesTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListAllowedDirectoriesTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListAllowedDirectoriesTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListDirectoryTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListDirectoryTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListDirectoryTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ListDirectoryTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/MoveFileTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/MoveFileTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/MoveFileTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/MoveFileTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadFileTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadFileTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadFileTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadFileTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadMultipleFilesTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadMultipleFilesTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadMultipleFilesTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/ReadMultipleFilesTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/SearchFilesTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/SearchFilesTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/SearchFilesTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/SearchFilesTool.java diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/WriteFileTool.java b/sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/WriteFileTool.java similarity index 100% rename from core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/WriteFileTool.java rename to sandbox-core/src/main/java/io/agentscope/runtime/sandbox/tools/fs/WriteFileTool.java diff --git a/core/src/main/resources/META-INF/services/io.agentscope.runtime.sandbox.manager.registry.SandboxProvider b/sandbox-core/src/main/resources/META-INF/services/io.agentscope.runtime.sandbox.manager.registry.SandboxProvider similarity index 100% rename from core/src/main/resources/META-INF/services/io.agentscope.runtime.sandbox.manager.registry.SandboxProvider rename to sandbox-core/src/main/resources/META-INF/services/io.agentscope.runtime.sandbox.manager.registry.SandboxProvider diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/DockerLifecycleTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/DockerLifecycleTest.java similarity index 58% rename from core/src/test/java/io/agentscope/runtime/sandbox/manager/DockerLifecycleTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/DockerLifecycleTest.java index 5c466686..e94e5c70 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/DockerLifecycleTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/DockerLifecycleTest.java @@ -16,23 +16,21 @@ package io.agentscope.runtime.sandbox.manager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; +import io.agentscope.runtime.sandbox.box.*; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; - -import static org.junit.jupiter.api.Assertions.*; - -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; import java.util.UUID; +import static org.junit.jupiter.api.Assertions.*; + // Todo: Currently only supports testing for local Docker environment + /** * Docker Sandbox Lifecycle Test * Tests sandbox creation, startup, status checking, stopping and cleanup functionality in Docker environment @@ -45,7 +43,7 @@ private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; private String testUserId; private String testSessionId; @@ -57,25 +55,25 @@ void setUp() { // Initialize Docker sandbox manager try { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - sandboxManager.start(); - System.out.println("Docker SandboxManager initialized successfully"); + sandboxService = new SandboxService(config); + sandboxService.start(); + System.out.println("Docker SandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize Docker SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize Docker SandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @AfterEach void tearDown() { - if (sandboxManager != null) { + if (sandboxService != null) { try { // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); @@ -90,18 +88,19 @@ void tearDown() { void testCreateAndStartBaseSandbox() { System.out.println("Testing BASE sandbox creation and startup..."); - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); + Sandbox sandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox, "Sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Sandbox container name should not be null"); + assertNotNull(sandbox.getSandboxId(), "Sandbox container ID should not be null"); - System.out.println("Created BASE sandbox: " + sandbox.getContainerId()); + System.out.println("Created BASE sandbox: " + sandbox.getSandboxId()); // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); + String status = sandboxService.getSandboxStatus(testUserId, testSessionId, "base"); assertNotNull(status, "Sandbox status should not be null"); System.out.println("BASE sandbox status: " + status); + + sandboxService.cleanupAllSandboxes(); } /** @@ -111,18 +110,18 @@ void testCreateAndStartBaseSandbox() { void testCreateAndStartBrowserSandbox() { System.out.println("Testing BROWSER sandbox creation and startup..."); - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); + Sandbox sandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox, "Browser sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Browser sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Browser sandbox container name should not be null"); + assertNotNull(sandbox.getSandboxId(), "Browser sandbox container ID should not be null"); - System.out.println("Created BROWSER sandbox: " + sandbox.getContainerId()); + System.out.println("Created BROWSER sandbox: " + sandbox.getSandboxId()); // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.BROWSER, testUserId, testSessionId); + String status = sandboxService.getSandboxStatus(testUserId, testSessionId, "browser"); assertNotNull(status, "Browser sandbox status should not be null"); System.out.println("BROWSER sandbox status: " + status); + sandboxService.cleanupAllSandboxes(); } /** @@ -132,18 +131,18 @@ void testCreateAndStartBrowserSandbox() { void testCreateAndStartFilesystemSandbox() { System.out.println("Testing FILESYSTEM sandbox creation and startup..."); - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.FILESYSTEM, testUserId, testSessionId); + Sandbox sandbox = new FilesystemSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox, "Filesystem sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Filesystem sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Filesystem sandbox container name should not be null"); + assertNotNull(sandbox.getSandboxId(), "Filesystem sandbox container ID should not be null"); - System.out.println("Created FILESYSTEM sandbox: " + sandbox.getContainerId()); + System.out.println("Created FILESYSTEM sandbox: " + sandbox.getSandboxId()); // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.FILESYSTEM, testUserId, testSessionId); + String status = sandboxService.getSandboxStatus(testUserId, testSessionId, "filesystem"); assertNotNull(status, "Filesystem sandbox status should not be null"); System.out.println("FILESYSTEM sandbox status: " + status); + sandboxService.cleanupAllSandboxes(); } /** @@ -153,18 +152,17 @@ void testCreateAndStartFilesystemSandbox() { void testCreateAndStartTrainingSandbox() { System.out.println("Testing TRAINING sandbox creation and startup..."); - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.TRAINING, testUserId, testSessionId); + Sandbox sandbox = new TrainingSandbox(sandboxService, testUserId, testSessionId, "training"); assertNotNull(sandbox, "Training sandbox should be created successfully"); - assertNotNull(sandbox.getContainerId(), "Training sandbox container ID should not be null"); - assertNotNull(sandbox.getContainerName(), "Training sandbox container name should not be null"); - System.out.println("Created TRAINING sandbox: " + sandbox.getContainerId()); + System.out.println("Created TRAINING sandbox: " + sandbox.getSandboxId()); // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.TRAINING, testUserId, testSessionId); + String status = sandboxService.getSandboxStatus(testUserId, testSessionId, "training"); assertNotNull(status, "Training sandbox status should not be null"); System.out.println("TRAINING sandbox status: " + status); + sandboxService.cleanupAllSandboxes(); } /** @@ -174,11 +172,10 @@ void testCreateAndStartTrainingSandbox() { void testMultipleSandboxesConcurrently() { System.out.println("Testing multiple sandboxes running concurrently..."); - // Create multiple sandboxes of different types - ContainerModel baseSandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - ContainerModel browserSandbox = sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); - ContainerModel filesystemSandbox = sandboxManager.getSandbox(SandboxType.FILESYSTEM, testUserId, testSessionId); - ContainerModel trainingSandbox = sandboxManager.getSandbox(SandboxType.TRAINING, testUserId, testSessionId); + Sandbox baseSandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Sandbox browserSandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); + Sandbox filesystemSandbox = new FilesystemSandbox(sandboxService, testUserId, testSessionId); + Sandbox trainingSandbox = new TrainingSandbox(sandboxService, testUserId, testSessionId, "training"); // Verify all sandboxes are created successfully assertNotNull(baseSandbox, "Base sandbox should be created successfully"); @@ -187,26 +184,28 @@ void testMultipleSandboxesConcurrently() { assertNotNull(trainingSandbox, "Training sandbox should be created successfully"); // Verify all sandboxes have different container IDs - assertNotEquals(baseSandbox.getContainerId(), browserSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - assertNotEquals(baseSandbox.getContainerId(), filesystemSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - assertNotEquals(browserSandbox.getContainerId(), filesystemSandbox.getContainerId(), "Different sandboxes should have different container IDs"); - assertNotEquals(trainingSandbox.getContainerId(), baseSandbox.getContainerId(), "Different sandboxes should have different container IDs"); + assertNotEquals(baseSandbox.getSandboxId(), browserSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + assertNotEquals(baseSandbox.getSandboxId(), filesystemSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + assertNotEquals(browserSandbox.getSandboxId(), filesystemSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + assertNotEquals(trainingSandbox.getSandboxId(), baseSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); System.out.println("All sandboxes created successfully:"); - System.out.println("- BASE: " + baseSandbox.getContainerId()); - System.out.println("- BROWSER: " + browserSandbox.getContainerId()); - System.out.println("- FILESYSTEM: " + filesystemSandbox.getContainerId()); - System.out.println("- TRAINING: " + trainingSandbox.getContainerId()); + System.out.println("- BASE: " + baseSandbox.getSandboxId()); + System.out.println("- BROWSER: " + browserSandbox.getSandboxId()); + System.out.println("- FILESYSTEM: " + filesystemSandbox.getSandboxId()); + System.out.println("- TRAINING: " + trainingSandbox.getSandboxId()); // Check all sandbox statuses - String baseStatus = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); - String browserStatus = sandboxManager.getSandboxStatus(SandboxType.BROWSER, testUserId, testSessionId); - String filesystemStatus = sandboxManager.getSandboxStatus(SandboxType.FILESYSTEM, testUserId, testSessionId); - String trainingStatus = sandboxManager.getSandboxStatus(SandboxType.TRAINING, testUserId, testSessionId); + String baseStatus = sandboxService.getSandboxStatus(testUserId, testSessionId, "base"); + String browserStatus = sandboxService.getSandboxStatus(testUserId, testSessionId, "browser"); + String filesystemStatus = sandboxService.getSandboxStatus(testUserId, testSessionId, "filesystem"); + String trainingStatus = sandboxService.getSandboxStatus(testUserId, testSessionId, "training"); assertNotNull(baseStatus, "Base sandbox status should not be null"); assertNotNull(browserStatus, "Browser sandbox status should not be null"); assertNotNull(filesystemStatus, "Filesystem sandbox status should not be null"); assertNotNull(trainingStatus, "Training sandbox status should not be null"); + + sandboxService.cleanupAllSandboxes(); } /** @@ -217,15 +216,17 @@ void testSandboxStatusCheck() { System.out.println("Testing sandbox status check..."); // Create sandbox - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); + Sandbox sandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox, "Sandbox should be created successfully"); // Check sandbox status - String status = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); + String status = sandboxService.getSandboxStatus(testUserId, testSessionId, "base"); assertNotNull(status, "Sandbox status should not be null"); assertNotEquals("not_found", status, "Sandbox status should not be 'not_found'"); System.out.println("Sandbox status: " + status); + + sandboxService.cleanupAllSandboxes(); } /** @@ -236,21 +237,23 @@ void testStopAndRemoveSandbox() { System.out.println("Testing sandbox stop and removal..."); // Create sandbox - ContainerModel sandbox = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); + Sandbox sandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox, "Sandbox should be created successfully"); - String containerId = sandbox.getContainerId(); + String containerId = sandbox.getSandboxId(); System.out.println("Created sandbox: " + containerId); // Stop and remove sandbox - sandboxManager.stopAndRemoveSandbox(SandboxType.BASE, testUserId, testSessionId); + boolean result = sandboxService.stopAndRemoveSandbox(testUserId, testSessionId, "base"); System.out.println("Sandbox stopped and removed"); // Verify sandbox has been deleted - String status = sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId); + String status = sandboxService.getSandboxStatus(testUserId, testSessionId, "base"); assertEquals("not_found", status, "Sandbox status should be 'not_found' indicating it has been deleted"); System.out.println("Sandbox successfully removed, status: " + status); + + sandboxService.cleanupAllSandboxes(); } /** @@ -261,22 +264,24 @@ void testCleanupAllSandboxes() { System.out.println("Testing cleanup of all sandboxes..."); // Create multiple sandboxes - sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); - sandboxManager.getSandbox(SandboxType.FILESYSTEM, testUserId, testSessionId); + Sandbox baseSandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Sandbox browserSandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); + Sandbox filesystemSandbox = new FilesystemSandbox(sandboxService, testUserId, testSessionId); System.out.println("Created multiple sandboxes"); // Clean up all sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All sandboxes cleaned up"); // Verify all sandboxes have been deleted - assertEquals("not_found", sandboxManager.getSandboxStatus(SandboxType.BASE, testUserId, testSessionId)); - assertEquals("not_found", sandboxManager.getSandboxStatus(SandboxType.BROWSER, testUserId, testSessionId)); - assertEquals("not_found", sandboxManager.getSandboxStatus(SandboxType.FILESYSTEM, testUserId, testSessionId)); + assertEquals("not_found", sandboxService.getSandboxStatus(testUserId, testSessionId, "base")); + assertEquals("not_found", sandboxService.getSandboxStatus(testUserId, testSessionId, "browser")); + assertEquals("not_found", sandboxService.getSandboxStatus(testUserId, testSessionId, "filesystem")); System.out.println("All sandboxes successfully cleaned up"); + + sandboxService.cleanupAllSandboxes(); } /** @@ -286,17 +291,19 @@ void testCleanupAllSandboxes() { void testGetAllSandboxes() { System.out.println("Testing get all sandboxes..."); - // Create some sandboxes - sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); - sandboxManager.getSandbox(SandboxType.BROWSER, testUserId, testSessionId); + // Create multiple sandboxes + Sandbox baseSandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Sandbox browserSandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); // Get all sandbox information - var allSandboxes = sandboxManager.getAllSandboxes(); + var allSandboxes = sandboxService.getAllSandboxes(); assertNotNull(allSandboxes, "Sandbox mapping should not be null"); assertTrue(allSandboxes.size() >= 2, "Should have at least 2 sandboxes"); System.out.println("Total sandboxes: " + allSandboxes.size()); allSandboxes.forEach((key, model) -> System.out.println("Sandbox: " + key + " -> " + model.getContainerId())); + + sandboxService.cleanupAllSandboxes(); } /** @@ -307,14 +314,16 @@ void testDuplicateSandboxCreation() { System.out.println("Testing duplicate sandbox creation..."); // Create first sandbox - ContainerModel sandbox1 = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); + Sandbox sandbox1 = new BaseSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox1, "First sandbox should be created successfully"); // Try to create sandbox of same type (should return existing sandbox) - ContainerModel sandbox2 = sandboxManager.getSandbox(SandboxType.BASE, testUserId, testSessionId); + Sandbox sandbox2 = new BaseSandbox(sandboxService, testUserId, testSessionId); assertNotNull(sandbox2, "Should return existing sandbox"); - assertEquals(sandbox1.getContainerId(), sandbox2.getContainerId(), "Duplicate creation should return the same sandbox instance"); + assertEquals(sandbox1.getSandboxId(), sandbox2.getSandboxId(), "Duplicate creation should return the same sandbox instance"); System.out.println("Duplicate sandbox creation handled correctly"); + + sandboxService.cleanupAllSandboxes(); } } diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/LocalFileSystemTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/LocalFileSystemTest.java similarity index 78% rename from core/src/test/java/io/agentscope/runtime/sandbox/manager/LocalFileSystemTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/LocalFileSystemTest.java index aff190f9..7a5be5ac 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/LocalFileSystemTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/LocalFileSystemTest.java @@ -16,10 +16,9 @@ package io.agentscope.runtime.sandbox.manager; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.model.fs.LocalFileSystemConfig; +import io.agentscope.runtime.sandbox.box.BaseSandbox; +import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.fs.local.LocalFileSystemConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterEach; @@ -52,7 +51,7 @@ private static boolean isCI() { } private String localStoragePath; - private SandboxManager manager; + private SandboxService sandboxService; @BeforeEach void setUp() { @@ -78,12 +77,12 @@ void setUp() { @AfterEach void tearDown() { - if (manager != null) { + if (sandboxService != null) { try { - manager.close(); - System.out.println("SandboxManager closed successfully"); + sandboxService.close(); + System.out.println("SandboxService closed successfully"); } catch (Exception e) { - System.err.println("Error closing SandboxManager: " + e.getMessage()); + System.err.println("Error closing SandboxService: " + e.getMessage()); } } } @@ -97,8 +96,8 @@ void testLocalFileSystemIntegration() { try { // 1. Prepare test data - create test folder and files under local storage path - String testFolderName = "test_session_folder"; - File testFolder = new File(localStoragePath, testFolderName); + String storagePath = "test_session_folder"; + File testFolder = new File(localStoragePath, storagePath); if (!testFolder.exists()) { assertTrue(testFolder.mkdirs(), "Should be able to create test folder"); } @@ -108,7 +107,7 @@ void testLocalFileSystemIntegration() { createInitialTestFiles(testFolder.getAbsolutePath()); // 2. Configure local file system storage - LocalFileSystemConfig localConfig = LocalFileSystemConfig.builder() + LocalFileSystemConfig localFileSystemStarter = LocalFileSystemConfig.builder() .storageFolderPath(localStoragePath) .build(); @@ -116,44 +115,28 @@ void testLocalFileSystemIntegration() { // 3. Create ManagerConfig ManagerConfig config = new ManagerConfig.Builder() - .poolSize(0) - .fileSystemConfig(localConfig) .build(); - // 4. Create SandboxManager - manager = new SandboxManager(config); - manager.start(); - System.out.println("\nSandboxManager with local file system storage created successfully"); + // 4. Create SandboxService + sandboxService = new SandboxService(config); + sandboxService.start(); + System.out.println("\nSandboxService with local file system storage created successfully"); // 5. Create container (will copy data from local storage) System.out.println("\n--- Creating container ---"); - String storagePath = testFolderName; // Use the just created test folder + // Use the just created test folder System.out.println("Copying from local path: " + storagePath); - ContainerModel container = manager.getSandbox( - SandboxType.BASE, - null, - storagePath, - null, // environment - "local_test_user", - "local_test_session_001" - ); + Sandbox sandbox = new BaseSandbox(sandboxService, "test-user", "test-session", localFileSystemStarter); // Verify container created successfully - assertNotNull(container, "Container creation should succeed"); - assertNotNull(container.getContainerName(), "Container name should not be null"); - assertNotNull(container.getMountDir(), "Mount directory should not be null"); - assertEquals(storagePath, container.getStoragePath(), "Storage path should match"); - - System.out.println("\nContainer created with local file system storage:"); - System.out.println(" - Container: " + container.getContainerName()); - System.out.println(" - Mount Dir: " + container.getMountDir()); - System.out.println(" - Storage Path: " + container.getStoragePath()); + assertNotNull(sandbox, "Container creation should succeed"); + assertNotNull(sandbox.getSandboxId(), "Container ID should not be null"); // 6. Verify copied files System.out.println("\n--- Verifying files copied from local storage ---"); - File mountDirectory = new File(container.getMountDir()); + File mountDirectory = new File(localFileSystemStarter.getMountDir()); assertTrue(mountDirectory.exists(), "Mount directory should exist"); System.out.println("Files in mount directory:"); @@ -161,7 +144,7 @@ void testLocalFileSystemIntegration() { // 7. Create new files in container (for testing upload functionality) System.out.println("\n--- Creating new files in container ---"); - createTestFilesInContainer(container.getMountDir()); + createTestFilesInContainer(localFileSystemStarter.getMountDir()); // 8. Display file list again System.out.println("\nUpdated file list:"); @@ -169,9 +152,9 @@ void testLocalFileSystemIntegration() { // 9. Destroy container and upload data System.out.println("\n--- Destroying container and copying data back to local storage ---"); - boolean released = manager.release(container.getContainerName()); + boolean released = sandboxService.stopAndRemoveSandbox(sandbox.getSandboxId()); assertTrue(released, "Container should be released successfully"); - System.out.println("Container destroyed, data copied back to local storage: " + container.getStoragePath()); + System.out.println("Container destroyed, data copied back to local storage: " + localFileSystemStarter.getStorageFolderPath()); // 10. Verify data has been copied back to local storage System.out.println("\n--- Verifying files in local storage ---"); @@ -193,18 +176,18 @@ void testLocalFileSystemConfigValidation() { System.out.println("\n--- Testing Local File System Config Validation ---"); // Create local file system configuration - LocalFileSystemConfig localConfig = LocalFileSystemConfig.builder() + LocalFileSystemConfig localFileSystemStarter = LocalFileSystemConfig.builder() .storageFolderPath(localStoragePath) .mountDir("custom_mount_dir") .build(); // Validate configuration - assertEquals(localStoragePath, localConfig.getStorageFolderPath(), "Storage path should match"); - assertEquals("custom_mount_dir", localConfig.getMountDir(), "Mount directory should match"); + assertEquals(localStoragePath, localFileSystemStarter.getStorageFolderPath(), "Storage path should match"); + assertEquals("custom_mount_dir", localFileSystemStarter.getMountDir(), "Mount directory should match"); System.out.println("Local file system configuration validated successfully"); - System.out.println(" - Storage Path: " + localConfig.getStorageFolderPath()); - System.out.println(" - Mount Dir: " + localConfig.getMountDir()); + System.out.println(" - Storage Path: " + localFileSystemStarter.getStorageFolderPath()); + System.out.println(" - Mount Dir: " + localFileSystemStarter.getMountDir()); } /** diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/manager/OssStorageTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/OssStorageTest.java similarity index 72% rename from core/src/test/java/io/agentscope/runtime/sandbox/manager/OssStorageTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/OssStorageTest.java index cde7f4a4..6a8561cc 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/manager/OssStorageTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/OssStorageTest.java @@ -16,13 +16,13 @@ package io.agentscope.runtime.sandbox.manager; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; -import io.agentscope.runtime.sandbox.manager.model.container.SandboxType; -import io.agentscope.runtime.sandbox.manager.model.fs.OssConfig; +import io.agentscope.runtime.sandbox.box.BaseSandbox; +import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.fs.oss.OssConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.condition.EnabledIf; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; @@ -30,7 +30,7 @@ import java.io.FileWriter; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * OSS Storage Functionality Test @@ -58,7 +58,7 @@ private static boolean isCI() { private String ossAccessKeyId; private String ossAccessKeySecret; private String ossBucketName; - private SandboxManager manager; + private SandboxService sandboxService; @BeforeEach void setUp() { @@ -69,15 +69,15 @@ void setUp() { ossBucketName = System.getenv("OSS_BUCKET_NAME"); // Verify environment variables are configured - assertNotNull(ossEndpoint, "Environment variable OSS_ENDPOINT is not configured"); - assertNotNull(ossAccessKeyId, "Environment variable OSS_ACCESS_KEY_ID is not configured"); - assertNotNull(ossAccessKeySecret, "Environment variable OSS_ACCESS_KEY_SECRET is not configured"); - assertNotNull(ossBucketName, "Environment variable OSS_BUCKET_NAME is not configured"); + Assertions.assertNotNull(ossEndpoint, "Environment variable OSS_ENDPOINT is not configured"); + Assertions.assertNotNull(ossAccessKeyId, "Environment variable OSS_ACCESS_KEY_ID is not configured"); + Assertions.assertNotNull(ossAccessKeySecret, "Environment variable OSS_ACCESS_KEY_SECRET is not configured"); + Assertions.assertNotNull(ossBucketName, "Environment variable OSS_BUCKET_NAME is not configured"); - assertFalse(ossEndpoint.isEmpty(), "OSS_ENDPOINT cannot be empty"); - assertFalse(ossAccessKeyId.isEmpty(), "OSS_ACCESS_KEY_ID cannot be empty"); - assertFalse(ossAccessKeySecret.isEmpty(), "OSS_ACCESS_KEY_SECRET cannot be empty"); - assertFalse(ossBucketName.isEmpty(), "OSS_BUCKET_NAME cannot be empty"); + Assertions.assertFalse(ossEndpoint.isEmpty(), "OSS_ENDPOINT cannot be empty"); + Assertions.assertFalse(ossAccessKeyId.isEmpty(), "OSS_ACCESS_KEY_ID cannot be empty"); + Assertions.assertFalse(ossAccessKeySecret.isEmpty(), "OSS_ACCESS_KEY_SECRET cannot be empty"); + Assertions.assertFalse(ossBucketName.isEmpty(), "OSS_BUCKET_NAME cannot be empty"); System.out.println("OSS Configuration:"); System.out.println(" - Endpoint: " + ossEndpoint); @@ -87,12 +87,12 @@ void setUp() { @AfterEach void tearDown() { - if (manager != null) { + if (sandboxService != null) { try { - manager.close(); - System.out.println("SandboxManager closed successfully"); + sandboxService.close(); + System.out.println("SandboxService closed successfully"); } catch (Exception e) { - System.err.println("Error closing SandboxManager: " + e.getMessage()); + System.err.println("Error closing SandboxService: " + e.getMessage()); } } } @@ -118,13 +118,12 @@ void testOssStorageIntegration() { // 2. Create ManagerConfig ManagerConfig config = new ManagerConfig.Builder() - .poolSize(0) - .fileSystemConfig(ossConfig) + .baseUrl("http://localhost:10001") .build(); - // 3. Create SandboxManager - manager = new SandboxManager(config); - System.out.println("\nSandboxManager with OSS storage created successfully"); + // 3. Create SandboxService + sandboxService = new SandboxService(config); + sandboxService.start(); // 4. Create container (will download data from OSS) System.out.println("\n--- Creating container ---"); @@ -132,37 +131,23 @@ void testOssStorageIntegration() { String storagePath = "folder"; // Fixed: download all contents under folder/ System.out.println("Downloading from OSS path: " + storagePath); - ContainerModel container = manager.getSandbox( - SandboxType.BASE, - null, - storagePath, - null, // environment - "oss_test_user", - "oss_test_session_001" - ); + Sandbox sandbox = new BaseSandbox(sandboxService, "oss_test_user", "oss_test_session_001", ossConfig); // Verify container created successfully - assertNotNull(container, "Container creation should succeed"); - assertNotNull(container.getContainerName(), "Container name should not be null"); - assertNotNull(container.getMountDir(), "Mount directory should not be null"); - assertEquals(storagePath, container.getStoragePath(), "Storage path should match"); - - System.out.println("\nContainer created with OSS storage:"); - System.out.println(" - Container: " + container.getContainerName()); - System.out.println(" - Mount Dir: " + container.getMountDir()); - System.out.println(" - Storage Path: " + container.getStoragePath()); + Assertions.assertNotNull(sandbox, "Container creation should succeed"); + Assertions.assertNotNull(sandbox.getSandboxId(), "Container ID should not be null"); // 5. Verify downloaded files System.out.println("\n--- Verifying downloaded files from OSS ---"); - File mountDirectory = new File(container.getMountDir()); - assertTrue(mountDirectory.exists(), "Mount directory should exist"); + File mountDirectory = new File(ossConfig.getMountDir()); + Assertions.assertTrue(mountDirectory.exists(), "Mount directory should exist"); System.out.println("Files in mount directory:"); listFilesRecursively(mountDirectory, " "); // 6. Create new files in container (for testing upload functionality) System.out.println("\n--- Creating new files in container ---"); - createTestFilesInContainer(container.getMountDir()); + createTestFilesInContainer(ossConfig.getMountDir()); // 7. Display file list again System.out.println("\nUpdated file list:"); @@ -170,12 +155,12 @@ void testOssStorageIntegration() { // 8. Destroy container and upload data System.out.println("\n--- Destroying container and uploading data to OSS ---"); - boolean released = manager.release(container.getContainerName()); - assertTrue(released, "Container should be released successfully"); - System.out.println("Container destroyed, data uploaded to OSS: " + container.getStoragePath()); + boolean released = sandboxService.stopAndRemoveSandbox(sandbox.getSandboxId()); + Assertions.assertTrue(released, "Container should be released successfully"); + System.out.println("Container destroyed, data uploaded to OSS: " + ossConfig.getStorageFolderPath()); } catch (Exception e) { - fail("Test failed: " + e.getMessage(), e); + Assertions.fail("Test failed: " + e.getMessage(), e); } } @@ -233,7 +218,7 @@ private void createTestFilesInContainer(String mountDir) { // Create a subdirectory and file File subDir = new File(mountDir, "test_dir"); if (!subDir.exists()) { - assertTrue(subDir.mkdir(), "Should be able to create subdirectory"); + Assertions.assertTrue(subDir.mkdir(), "Should be able to create subdirectory"); } File subFile = new File(subDir, "nested_file.txt"); @@ -243,7 +228,7 @@ private void createTestFilesInContainer(String mountDir) { System.out.println(" Created nested file: test_dir/" + subFile.getName()); } catch (IOException e) { - fail("Failed to create test files: " + e.getMessage(), e); + Assertions.fail("Failed to create test files: " + e.getMessage(), e); } } diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/tools/BaseToolsTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BaseToolsTest.java similarity index 70% rename from core/src/test/java/io/agentscope/runtime/sandbox/tools/BaseToolsTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BaseToolsTest.java index e624c8a8..ca71d04c 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/tools/BaseToolsTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BaseToolsTest.java @@ -14,20 +14,21 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.tools; +package io.agentscope.runtime.sandbox.manager.tools; import io.agentscope.runtime.sandbox.box.BaseSandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; @EnabledIfDockerAvailable @EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") @@ -36,23 +37,23 @@ private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; private BaseSandbox sandbox; @BeforeEach void setUp() { // Initialize sandbox manager try { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); - ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); + ManagerConfig managerConfig = ManagerConfig.builder() + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - sandboxManager.start(); - sandbox = new BaseSandbox(sandboxManager, "test-user", "test-session"); - System.out.println("SandboxManager initialized successfully"); + SandboxService sandboxService = new SandboxService(managerConfig); + sandboxService.start(); + sandbox = new BaseSandbox(sandboxService, "test-user", "test-session"); + System.out.println("SandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize SandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @@ -66,10 +67,10 @@ void tearDown() { System.err.println("Error closing sandbox: " + e.getMessage()); } } - if (sandboxManager != null) { + if (sandboxService != null) { try { // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/tools/BrowserInteractiveToolsTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BrowserInteractiveToolsTest.java similarity index 89% rename from core/src/test/java/io/agentscope/runtime/sandbox/tools/BrowserInteractiveToolsTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BrowserInteractiveToolsTest.java index 37c8466e..dab93e9a 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/tools/BrowserInteractiveToolsTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BrowserInteractiveToolsTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.tools; +package io.agentscope.runtime.sandbox.manager.tools; import io.agentscope.runtime.sandbox.box.BrowserSandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,23 +40,23 @@ private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; private BrowserSandbox sandbox; @BeforeEach void setUp() { // Initialize sandbox manager try { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - sandboxManager.start(); - sandbox = new BrowserSandbox(sandboxManager, "test-user", "test-session"); - System.out.println("SandboxManager initialized successfully"); + sandboxService = new SandboxService(config); + sandboxService.start(); + sandbox = new BrowserSandbox(sandboxService, "test-user", "test-session"); + System.out.println("SandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize SandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @@ -70,10 +70,10 @@ void tearDown() { System.err.println("Error closing sandbox: " + e.getMessage()); } } - if (sandboxManager != null) { + if (sandboxService != null) { try { // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/tools/BrowserToolsTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BrowserToolsTest.java similarity index 89% rename from core/src/test/java/io/agentscope/runtime/sandbox/tools/BrowserToolsTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BrowserToolsTest.java index f136feec..17170e82 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/tools/BrowserToolsTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/BrowserToolsTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.tools; +package io.agentscope.runtime.sandbox.manager.tools; import io.agentscope.runtime.sandbox.box.BrowserSandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,23 +36,23 @@ private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; private BrowserSandbox sandbox; @BeforeEach void setUp() { // Initialize sandbox manager try { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - sandboxManager.start(); - sandbox = new BrowserSandbox(sandboxManager, "test-user", "test-session"); - System.out.println("SandboxManager initialized successfully"); + sandboxService = new SandboxService(config); + sandboxService.start(); + sandbox = new BrowserSandbox(sandboxService, "test-user", "test-session"); + System.out.println("SandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize SandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @@ -66,9 +66,9 @@ void tearDown() { System.err.println("Error closing sandbox: " + e.getMessage()); } } - if (sandboxManager != null) { + if (sandboxService != null) { try { - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/tools/FilesystemToolsTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/FilesystemToolsTest.java similarity index 86% rename from core/src/test/java/io/agentscope/runtime/sandbox/tools/FilesystemToolsTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/FilesystemToolsTest.java index 71c9afcf..8653eeda 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/tools/FilesystemToolsTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/tools/FilesystemToolsTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.tools; +package io.agentscope.runtime.sandbox.manager.tools; import io.agentscope.runtime.sandbox.box.FilesystemSandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.DockerClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,23 +36,23 @@ private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; private FilesystemSandbox sandbox; @BeforeEach void setUp() { // Initialize sandbox manager try { - BaseClientConfig clientConfig = DockerClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - sandboxManager.start(); - sandbox = new FilesystemSandbox(sandboxManager, "test-user", "test-session"); - System.out.println("SandboxManager initialized successfully"); + sandboxService = new SandboxService(config); + sandboxService.start(); + sandbox = new FilesystemSandbox(sandboxService, "test-user", "test-session"); + System.out.println("SandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize SandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @@ -66,10 +66,10 @@ void tearDown() { System.err.println("Error closing sandbox: " + e.getMessage()); } } - if (sandboxManager != null) { + if (sandboxService != null) { try { // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/training/AppWorldSandboxTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/training/AppWorldSandboxTest.java similarity index 88% rename from core/src/test/java/io/agentscope/runtime/sandbox/training/AppWorldSandboxTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/training/AppWorldSandboxTest.java index 73e7e85a..61b8af1f 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/training/AppWorldSandboxTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/training/AppWorldSandboxTest.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.training; +package io.agentscope.runtime.sandbox.manager.training; import com.fasterxml.jackson.core.JsonProcessingException; import io.agentscope.runtime.sandbox.box.APPWorldSandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -36,37 +36,38 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") -@EnabledIfEnvironmentVariable(named = "KUBECONFIG_PATH", matches = ".+") +@EnabledIfDockerAvailable public class AppWorldSandboxTest { private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; @BeforeEach void setUp() { // Initialize sandbox manager java.util.logging.Logger.getLogger("").setLevel(java.util.logging.Level.OFF); try { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().kubeConfigPath(System.getenv("KUBECONFIG_PATH")).build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - System.out.println("SandboxManager initialized successfully"); + sandboxService = new SandboxService(config); + sandboxService.start(); + System.out.println("SandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize SandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @AfterEach void tearDown() { - if (sandboxManager != null) { + if (sandboxService != null) { try { // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); @@ -76,7 +77,7 @@ void tearDown() { @Test public void testEnvProfiles() { - try(APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxManager, "test-user", "test-session")) { + try(APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "test-user", "test-session")) { String profiles = appWorldSandbox.getEnvProfile("appworld", "train", null); System.out.println("APPWorldSandbox env profiles: " + profiles); assertNotNull(profiles); @@ -87,7 +88,7 @@ public void testEnvProfiles() { @Test public void testInstance() throws JsonProcessingException { - try(APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxManager, "test-user", "test-session")) { + try(APPWorldSandbox appWorldSandbox = new APPWorldSandbox(sandboxService, "test-user", "test-session")) { String initResponse = appWorldSandbox.createInstance("appworld", "82e2fac_1", null, null); assertNotNull(initResponse); Map dataMap = parseTopLevel(initResponse); diff --git a/core/src/test/java/io/agentscope/runtime/sandbox/training/BfclSandboxTest.java b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/training/BfclSandboxTest.java similarity index 87% rename from core/src/test/java/io/agentscope/runtime/sandbox/training/BfclSandboxTest.java rename to sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/training/BfclSandboxTest.java index 592911b9..6c5cf653 100644 --- a/core/src/test/java/io/agentscope/runtime/sandbox/training/BfclSandboxTest.java +++ b/sandbox-core/src/test/java/io/agentscope/runtime/sandbox/manager/training/BfclSandboxTest.java @@ -14,21 +14,21 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.training; +package io.agentscope.runtime.sandbox.manager.training; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.agentscope.runtime.sandbox.box.BFCLSandbox; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; import java.util.List; import java.util.Map; @@ -36,13 +36,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") -@EnabledIfEnvironmentVariable(named = "KUBECONFIG_PATH", matches = ".+") +@EnabledIfDockerAvailable public class BfclSandboxTest { private static boolean isCI() { return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); } - private SandboxManager sandboxManager; + private SandboxService sandboxService; // Test data constants corresponding to Python ASSISTANT_MESSAGES private static final List> ASSISTANT_MESSAGES = List.of( @@ -99,24 +99,25 @@ void setUp() { // Initialize sandbox manager java.util.logging.Logger.getLogger("").setLevel(java.util.logging.Level.OFF); try { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().kubeConfigPath(System.getenv("KUBECONFIG_PATH")).build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig config = new ManagerConfig.Builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - sandboxManager = new SandboxManager(config); - System.out.println("SandboxManager initialized successfully"); + sandboxService = new SandboxService(config); + sandboxService.start(); + System.out.println("sandboxService initialized successfully"); } catch (Exception e) { - System.err.println("Failed to initialize SandboxManager: " + e.getMessage()); + System.err.println("Failed to initialize sandboxService: " + e.getMessage()); throw new RuntimeException("Failed to initialize test environment", e); } } @AfterEach void tearDown() { - if (sandboxManager != null) { + if (sandboxService != null) { try { // Clean up all test-created sandboxes - sandboxManager.cleanupAllSandboxes(); + sandboxService.cleanupAllSandboxes(); System.out.println("All test sandboxes cleaned up successfully"); } catch (Exception e) { System.err.println("Error during cleanup: " + e.getMessage()); @@ -126,7 +127,7 @@ void tearDown() { @Test public void testBfclSandbox() throws JsonProcessingException { - try(BFCLSandbox bfclSandbox = new BFCLSandbox(sandboxManager, "test-user", "test-session")) { + try(BFCLSandbox bfclSandbox = new BFCLSandbox(sandboxService, "test-user", "test-session")) { String envProfiles = bfclSandbox.getEnvProfile("bfcl", "train", null); System.out.println("BFCLSandbox env profiles: " + envProfiles); assertNotNull(envProfiles); diff --git a/sandbox-extensions/agentrun-extension/pom.xml b/sandbox-extensions/agentrun-extension/pom.xml new file mode 100644 index 00000000..7c48da0d --- /dev/null +++ b/sandbox-extensions/agentrun-extension/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + io.agentscope + agentscope-runtime + ${revision} + ../../pom.xml + + + agentrun-extension + + + 4.0.4 + + + + + io.agentscope + agentscope-runtime-sandbox-core + ${revision} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + com.aliyun + agentrun20250910 + ${agentrun.client.version} + + + + com.aliyun + alibabacloud-agentrun20250910 + ${agentrun.client.version} + + + + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/AgentRunClient.java b/sandbox-extensions/agentrun-extension/src/main/java/io/agentscope/runtime/sandbox/client/agentrun/AgentRunClient.java similarity index 98% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/AgentRunClient.java rename to sandbox-extensions/agentrun-extension/src/main/java/io/agentscope/runtime/sandbox/client/agentrun/AgentRunClient.java index a1149f28..9d3df785 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/AgentRunClient.java +++ b/sandbox-extensions/agentrun-extension/src/main/java/io/agentscope/runtime/sandbox/client/agentrun/AgentRunClient.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; +package io.agentscope.runtime.sandbox.client.agentrun; import com.aliyun.agentrun20250910.Client; import com.aliyun.agentrun20250910.models.*; import com.aliyun.teaopenapi.models.Config; -import io.agentscope.runtime.sandbox.manager.client.config.AgentRunClientConfig; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.ContainerCreateResult; import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +49,7 @@ public class AgentRunClient extends BaseClient { @SuppressWarnings("unused") private static final int HTTPS_PORT = 443; - private AgentRunClientConfig config; + private AgentRunClientStarter config; private Client client; private AgentRunSessionManager sessionManager; private String agentRunPrefix; @@ -56,7 +57,7 @@ public class AgentRunClient extends BaseClient { private int getAgentRuntimeStatusInterval; private boolean connected = false; - public AgentRunClient(AgentRunClientConfig config) throws Exception { + public AgentRunClient(AgentRunClientStarter config) throws Exception { this.config = config; this.client = createAgentRunClient(); this.sessionManager = new AgentRunSessionManager(); @@ -86,8 +87,8 @@ public boolean isConnected() { @Override public ContainerCreateResult createContainer(String containerName, String imageName, List ports, - List volumeBindings, - Map environment, Map runtimeConfig) { + List volumeBindings, + Map environment, Map runtimeConfig) { logger.info("Creating AgentRun session with image: {}", imageName); @SuppressWarnings("unused") diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/AgentRunClientConfig.java b/sandbox-extensions/agentrun-extension/src/main/java/io/agentscope/runtime/sandbox/client/agentrun/AgentRunClientStarter.java similarity index 83% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/AgentRunClientConfig.java rename to sandbox-extensions/agentrun-extension/src/main/java/io/agentscope/runtime/sandbox/client/agentrun/AgentRunClientStarter.java index 09cca1e5..1fade1d4 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/AgentRunClientConfig.java +++ b/sandbox-extensions/agentrun-extension/src/main/java/io/agentscope/runtime/sandbox/client/agentrun/AgentRunClientStarter.java @@ -14,9 +14,14 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client.config; +package io.agentscope.runtime.sandbox.client.agentrun; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerManagerType; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerClientType; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; @@ -24,7 +29,8 @@ * Configuration for AgentRun client. * Uses Builder pattern for convenient configuration. */ -public class AgentRunClientConfig extends BaseClientConfig { +public class AgentRunClientStarter extends BaseClientStarter { + Logger logger = LoggerFactory.getLogger(AgentRunClientStarter.class); private final String agentRunAccessKeyId; private final String agentRunAccessKeySecret; @@ -39,8 +45,8 @@ public class AgentRunClientConfig extends BaseClientConfig { private final String agentrunLogProject; private final String agentrunLogStore; - private AgentRunClientConfig(Builder builder) { - super(ContainerManagerType.AGENTRUN); + private AgentRunClientStarter(Builder builder) { + super(ContainerClientType.AGENTRUN); this.agentRunAccessKeyId = builder.agentRunAccessKeyId; this.agentRunAccessKeySecret = builder.agentRunAccessKeySecret; this.agentRunAccountId = builder.agentRunAccountId; @@ -108,6 +114,18 @@ public static Builder builder() { return new Builder(); } + @Override + public BaseClient startClient(PortManager portManager) { + try{ + AgentRunClient agentRunClient = new AgentRunClient(this); + agentRunClient.connect(); + logger.info("AgentRun client created"); + return agentRunClient; + } catch (Exception e) { + throw new RuntimeException("Failed to initialize agentrun client"); + } + } + public static class Builder { private String agentRunAccessKeyId; private String agentRunAccessKeySecret; @@ -182,8 +200,8 @@ public Builder agentrunLogStore(String agentrunLogStore) { return this; } - public AgentRunClientConfig build() { - return new AgentRunClientConfig(this); + public AgentRunClientStarter build() { + return new AgentRunClientStarter(this); } } } diff --git a/sandbox-extensions/fc-extension/pom.xml b/sandbox-extensions/fc-extension/pom.xml new file mode 100644 index 00000000..109e894c --- /dev/null +++ b/sandbox-extensions/fc-extension/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + io.agentscope + agentscope-runtime + ${revision} + ../../pom.xml + + + fc-extension + + + + io.agentscope + agentscope-runtime-sandbox-core + ${revision} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + com.aliyun + alibabacloud-fc20230330 + 4.0.26 + + + + com.aliyun + fc20230330 + 4.6.4 + + + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/FcClient.java b/sandbox-extensions/fc-extension/src/main/java/io/agentscope/runtime/sandbox/client/fc/FcClient.java similarity index 98% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/FcClient.java rename to sandbox-extensions/fc-extension/src/main/java/io/agentscope/runtime/sandbox/client/fc/FcClient.java index 0279892f..70bdd23d 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/FcClient.java +++ b/sandbox-extensions/fc-extension/src/main/java/io/agentscope/runtime/sandbox/client/fc/FcClient.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; +package io.agentscope.runtime.sandbox.client.fc; import com.aliyun.fc20230330.Client; import com.aliyun.fc20230330.models.*; import com.aliyun.teaopenapi.models.Config; import com.aliyun.teautil.models.RuntimeOptions; -import io.agentscope.runtime.sandbox.manager.client.config.FcClientConfig; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.ContainerCreateResult; import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,13 +41,13 @@ public class FcClient extends BaseClient { private static final int HTTPS_PORT = 443; private Client fcClient; - private FcClientConfig fcClientConfig; + private FcClientStarter fcClientConfig; private FCSessionManager fcSessionManager; private String functionPrefix; private boolean connected = false; private final SecureRandom random = new SecureRandom(); - public FcClient(FcClientConfig fcClientConfig) throws Exception { + public FcClient(FcClientStarter fcClientConfig) throws Exception { this.fcClientConfig = fcClientConfig; this.fcSessionManager = new FCSessionManager(); this.functionPrefix = fcClientConfig.getFcPrefix() != null ? fcClientConfig.getFcPrefix() : "agentscope-sandbox"; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/FcClientConfig.java b/sandbox-extensions/fc-extension/src/main/java/io/agentscope/runtime/sandbox/client/fc/FcClientStarter.java similarity index 82% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/FcClientConfig.java rename to sandbox-extensions/fc-extension/src/main/java/io/agentscope/runtime/sandbox/client/fc/FcClientStarter.java index 452af2e5..c69cdec2 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/FcClientConfig.java +++ b/sandbox-extensions/fc-extension/src/main/java/io/agentscope/runtime/sandbox/client/fc/FcClientStarter.java @@ -14,13 +14,19 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client.config; +package io.agentscope.runtime.sandbox.client.fc; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerManagerType; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerClientType; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; -public class FcClientConfig extends BaseClientConfig { +public class FcClientStarter extends BaseClientStarter { + Logger logger = LoggerFactory.getLogger(FcClientStarter.class); private final String FcAccessKeyId; private final String FcAccessKeySecret; private final String FcAccountId; @@ -34,8 +40,8 @@ public class FcClientConfig extends BaseClientConfig { private final String FcLogProject; private final String FcLogStore; - private FcClientConfig(Builder builder) { - super(ContainerManagerType.FC); + private FcClientStarter(Builder builder) { + super(ContainerClientType.FC); this.FcAccessKeyId = builder.FcAccessKeyId; this.FcAccessKeySecret = builder.FcAccessKeySecret; this.FcAccountId = builder.FcAccountId; @@ -102,6 +108,19 @@ public static Builder builder() { return new Builder(); } + @Override + public BaseClient startClient(PortManager portManager) { + try{ + FcClient fcClient = new FcClient(this); + fcClient.connect(); + logger.info("FC client created"); + return fcClient; + } catch (Exception e) { + throw new RuntimeException("Failed to initialize FC client"); + } + + } + public static class Builder { private String FcAccessKeyId; private String FcAccessKeySecret; @@ -176,8 +195,8 @@ public Builder FcLogStore(String FcLogStore) { return this; } - public FcClientConfig build() { - return new FcClientConfig(this); + public FcClientStarter build() { + return new FcClientStarter(this); } } } diff --git a/sandbox-extensions/kubernetes-extension/pom.xml b/sandbox-extensions/kubernetes-extension/pom.xml new file mode 100644 index 00000000..f6706316 --- /dev/null +++ b/sandbox-extensions/kubernetes-extension/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + io.agentscope + agentscope-runtime + ${revision} + ../../pom.xml + + + kubernetes-extension + + + 25.0.0 + + + + + io.agentscope + agentscope-runtime-sandbox-core + ${revision} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + io.kubernetes + client-java-api + ${kubernetes.client.version} + + + io.kubernetes + client-java + ${kubernetes.client.version} + + + io.kubernetes + client-java-extended + ${kubernetes.client.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.testcontainers + junit-jupiter + test + + + + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/KubernetesClient.java b/sandbox-extensions/kubernetes-extension/src/main/java/io/agentscope/runtime/sandbox/client/kubernetes/KubernetesClient.java similarity index 99% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/KubernetesClient.java rename to sandbox-extensions/kubernetes-extension/src/main/java/io/agentscope/runtime/sandbox/client/kubernetes/KubernetesClient.java index cd460976..012c9d61 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/KubernetesClient.java +++ b/sandbox-extensions/kubernetes-extension/src/main/java/io/agentscope/runtime/sandbox/client/kubernetes/KubernetesClient.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client; +package io.agentscope.runtime.sandbox.client.kubernetes; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.ContainerCreateResult; import io.agentscope.runtime.sandbox.manager.model.fs.VolumeBinding; import io.kubernetes.client.custom.IntOrString; import io.kubernetes.client.openapi.ApiClient; @@ -44,7 +45,7 @@ public class KubernetesClient extends BaseClient { private AppsV1Api appsApi; private boolean connected = false; private String kubeconfigPath = null; - private KubernetesClientConfig config; + private KubernetesClientStarter config; private String namespace; public KubernetesClient() { @@ -55,7 +56,7 @@ public KubernetesClient(String kubeconfigPath) { this.namespace = DEFAULT_NAMESPACE; } - public KubernetesClient(KubernetesClientConfig config) { + public KubernetesClient(KubernetesClientStarter config) { this.config = config; this.kubeconfigPath = config.getKubeConfigPath(); this.namespace = config.getNamespace() != null ? config.getNamespace() : DEFAULT_NAMESPACE; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/KubernetesClientConfig.java b/sandbox-extensions/kubernetes-extension/src/main/java/io/agentscope/runtime/sandbox/client/kubernetes/KubernetesClientStarter.java similarity index 63% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/KubernetesClientConfig.java rename to sandbox-extensions/kubernetes-extension/src/main/java/io/agentscope/runtime/sandbox/client/kubernetes/KubernetesClientStarter.java index 2838e6ac..d75e4216 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/client/config/KubernetesClientConfig.java +++ b/sandbox-extensions/kubernetes-extension/src/main/java/io/agentscope/runtime/sandbox/client/kubernetes/KubernetesClientStarter.java @@ -14,22 +14,28 @@ * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.client.config; +package io.agentscope.runtime.sandbox.client.kubernetes; -import io.agentscope.runtime.sandbox.manager.model.container.ContainerManagerType; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClient; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerClientType; +import io.agentscope.runtime.sandbox.manager.utils.PortManager; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class KubernetesClientConfig extends BaseClientConfig { +public class KubernetesClientStarter extends BaseClientStarter { + Logger logger = LoggerFactory.getLogger(KubernetesClientStarter.class); private static final String KUBE_CONFIG_PATH = System.getProperty("user.home") + "/.kube/config"; private String kubeConfigPath; private String namespace; - private KubernetesClientConfig(){ - super(ContainerManagerType.KUBERNETES); + private KubernetesClientStarter(){ + super(ContainerClientType.KUBERNETES); } - private KubernetesClientConfig(String kubeConfigPath, String namespace) { - super(ContainerManagerType.KUBERNETES); + private KubernetesClientStarter(String kubeConfigPath, String namespace) { + super(ContainerClientType.KUBERNETES); this.kubeConfigPath = StringUtils.isEmpty(kubeConfigPath) ? KUBE_CONFIG_PATH : kubeConfigPath; this.namespace = namespace; } @@ -54,6 +60,14 @@ public void setKubeConfigPath(String kubeConfigPath) { this.kubeConfigPath = kubeConfigPath; } + @Override + public BaseClient startClient(PortManager portManager) { + KubernetesClient kubernetesClient = new KubernetesClient(this); + kubernetesClient.connect(); + logger.info("Kubernetes client started."); + return kubernetesClient; + } + public static class Builder { private String kubeConfigPath; private String namespace = "default"; @@ -71,8 +85,8 @@ public Builder namespace(String namespace) { return this; } - public KubernetesClientConfig build() { - return new KubernetesClientConfig(kubeConfigPath, namespace); + public KubernetesClientStarter build() { + return new KubernetesClientStarter(kubeConfigPath, namespace); } } } diff --git a/sandbox-extensions/kubernetes-extension/src/test/java/io/agentscope/runtime/sandbox/client/kubernetes/K8sSandboxLifecycleTest.java b/sandbox-extensions/kubernetes-extension/src/test/java/io/agentscope/runtime/sandbox/client/kubernetes/K8sSandboxLifecycleTest.java new file mode 100644 index 00000000..2277add4 --- /dev/null +++ b/sandbox-extensions/kubernetes-extension/src/test/java/io/agentscope/runtime/sandbox/client/kubernetes/K8sSandboxLifecycleTest.java @@ -0,0 +1,292 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.client.kubernetes; + +import io.agentscope.runtime.sandbox.box.*; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; + +import java.util.UUID; + +/** + * Kubernetes Sandbox Lifecycle Test + * Tests sandbox creation, startup, status checking, stopping and cleanup functionality in Kubernetes environment + */ +@EnabledIfEnvironmentVariable(named = "KUBECONFIG_PATH", matches = ".+") +@EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") +public class K8sSandboxLifecycleTest { + private static boolean isCI() { + return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); + } + + private SandboxService sandboxService; + private String testUserId; + private String testSessionId; + + @BeforeEach + void setUp() { + // Generate test user ID and session ID + testUserId = "test-user-" + UUID.randomUUID().toString().substring(0, 8); + testSessionId = "test-session-" + UUID.randomUUID().toString().substring(0, 8); + + // Initialize Kubernetes sandbox manager + try { + BaseClientStarter clientConfig = KubernetesClientStarter.builder().kubeConfigPath(System.getenv("KUBECONFIG_PATH")).build(); + System.out.println(System.getenv("KUBECONFIG_PATH")); + ManagerConfig config = new ManagerConfig.Builder() + .clientStarter(clientConfig) + .build(); + sandboxService = new SandboxService(config); + sandboxService.start(); + System.out.println("Kubernetes SandboxService initialized successfully"); + } catch (Exception e) { + System.err.println("Failed to initialize Kubernetes SandboxService: " + e.getMessage()); + throw new RuntimeException("Failed to initialize test environment", e); + } + } + + @AfterEach + void tearDown() { + if (sandboxService != null) { + try { + // Clean up all test-created sandboxes + sandboxService.cleanupAllSandboxes(); + System.out.println("All test sandboxes cleaned up successfully"); + } catch (Exception e) { + System.err.println("Error during cleanup: " + e.getMessage()); + } + } + } + + /** + * Test base sandbox creation and startup + */ + @Test + void testCreateAndStartBaseSandbox() { + System.out.println("Testing BASE sandbox creation and startup..."); + + + Sandbox sandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + + Assertions.assertNotNull(sandbox, "Sandbox should be created successfully"); + Assertions.assertNotNull(sandbox.getSandboxId(), "Sandbox container ID should not be null"); + + System.out.println("Created BASE sandbox: " + sandbox.getSandboxId()); + + // Check sandbox status + String status = sandboxService.getSandboxStatus(sandbox.getSandboxId()); + Assertions.assertNotNull(status, "Sandbox status should not be null"); + System.out.println("BASE sandbox status: " + status); + } + + /** + * Test browser sandbox creation and startup + */ + @Test + void testCreateAndStartBrowserSandbox() { + System.out.println("Testing BROWSER sandbox creation and startup..."); + + Sandbox sandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); + + Assertions.assertNotNull(sandbox, "Browser sandbox should be created successfully"); + Assertions.assertNotNull(sandbox.getSandboxId(), "Browser sandbox container ID should not be null"); + + System.out.println("Created BROWSER sandbox: " + sandbox.getSandboxId()); + + // Check sandbox status + String status = sandboxService.getSandboxStatus(sandbox.getSandboxId()); + Assertions.assertNotNull(status, "Browser sandbox status should not be null"); + System.out.println("BROWSER sandbox status: " + status); + } + + /** + * Test filesystem sandbox creation and startup + */ + @Test + void testCreateAndStartFilesystemSandbox() { + System.out.println("Testing FILESYSTEM sandbox creation and startup..."); + + Sandbox sandbox = new FilesystemSandbox(sandboxService, testUserId, testSessionId); + + Assertions.assertNotNull(sandbox, "Filesystem sandbox should be created successfully"); + Assertions.assertNotNull(sandbox.getSandboxId(), "Filesystem sandbox container ID should not be null"); + + System.out.println("Created FILESYSTEM sandbox: " + sandbox.getSandboxId()); + // Check sandbox status + String status = sandboxService.getSandboxStatus(sandbox.getSandboxId()); + Assertions.assertNotNull(status, "Filesystem sandbox status should not be null"); + System.out.println("FILESYSTEM sandbox status: " + status); + } + + /** + * Test multiple sandboxes running concurrently + */ + @Test + void testMultipleSandboxesConcurrently() { + System.out.println("Testing multiple sandboxes running concurrently..."); + + // Create multiple sandboxes of different types + Sandbox baseSandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Sandbox browserSandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); + Sandbox filesystemSandbox = new FilesystemSandbox(sandboxService, testUserId, testSessionId); + Sandbox trainingSandbox = new BFCLSandbox(sandboxService, testUserId, testSessionId); + + // Verify all sandboxes are created successfully + Assertions.assertNotNull(baseSandbox, "Base sandbox should be created successfully"); + Assertions.assertNotNull(browserSandbox, "Browser sandbox should be created successfully"); + Assertions.assertNotNull(filesystemSandbox, "Filesystem sandbox should be created successfully"); + Assertions.assertNotNull(trainingSandbox, "Training sandbox should be created successfully"); + + // Verify all sandboxes have different container IDs + Assertions.assertNotEquals(baseSandbox.getSandboxId(), browserSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + Assertions.assertNotEquals(baseSandbox.getSandboxId(), filesystemSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + Assertions.assertNotEquals(browserSandbox.getSandboxId(), filesystemSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + Assertions.assertNotEquals(trainingSandbox.getSandboxId(), baseSandbox.getSandboxId(), "Different sandboxes should have different container IDs"); + + System.out.println("All sandboxes created successfully:"); + System.out.println("- BASE: " + baseSandbox.getSandboxId()); + System.out.println("- BROWSER: " + browserSandbox.getSandboxId()); + System.out.println("- FILESYSTEM: " + filesystemSandbox.getSandboxId()); + System.out.println("- TRAINING: " + trainingSandbox.getSandboxId()); + + // Check all sandbox statuses + String baseStatus = sandboxService.getSandboxStatus(baseSandbox.getSandboxId()); + String browserStatus = sandboxService.getSandboxStatus(browserSandbox.getSandboxId()); + String filesystemStatus = sandboxService.getSandboxStatus(filesystemSandbox.getSandboxId()); + String trainingStatus = sandboxService.getSandboxStatus(trainingSandbox.getSandboxId()); + Assertions.assertNotNull(baseStatus, "Base sandbox status should not be null"); + Assertions.assertNotNull(browserStatus, "Browser sandbox status should not be null"); + Assertions.assertNotNull(filesystemStatus, "Filesystem sandbox status should not be null"); + Assertions.assertNotNull(trainingStatus, "Training sandbox status should not be null"); + } + + /** + * Test sandbox status checking + */ + @Test + void testSandboxStatusCheck() { + System.out.println("Testing sandbox status check..."); + + // Create sandbox + Sandbox sandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Assertions.assertNotNull(sandbox, "Sandbox should be created successfully"); + + // Check sandbox status + String status = sandboxService.getSandboxStatus(sandbox.getSandboxId()); + Assertions.assertNotNull(status, "Sandbox status should not be null"); + Assertions.assertNotEquals("not_found", status, "Sandbox status should not be 'not_found'"); + + System.out.println("Sandbox status: " + status); + } + + /** + * Test sandbox stopping and cleanup + */ + @Test + void testStopAndRemoveSandbox() { + System.out.println("Testing sandbox stop and removal..."); + + // Create sandbox + Sandbox sandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Assertions.assertNotNull(sandbox, "Sandbox should be created successfully"); + + String containerId = sandbox.getSandboxId(); + System.out.println("Created sandbox: " + containerId); + + // Stop and remove sandbox + sandboxService.stopAndRemoveSandbox(sandbox.getSandboxId()); + System.out.println("Sandbox stopped and removed"); + + // Verify sandbox has been deleted + String status = sandboxService.getSandboxStatus(sandbox.getSandboxId()); + Assertions.assertEquals("not_found", status, "Sandbox status should be 'not_found' indicating it has been deleted"); + + System.out.println("Sandbox successfully removed, status: " + status); + } + + /** + * Test cleanup of all sandboxes + */ + @Test + void testCleanupAllSandboxes() { + System.out.println("Testing cleanup of all sandboxes..."); + + // Create multiple sandboxes + Sandbox baseSandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Sandbox browserSandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); + Sandbox fileSystemSandbox = new FilesystemSandbox(sandboxService, testUserId, testSessionId); + + System.out.println("Created multiple sandboxes"); + + // Clean up all sandboxes + sandboxService.cleanupAllSandboxes(); + System.out.println("All sandboxes cleaned up"); + + // Verify all sandboxes have been deleted + Assertions.assertEquals("not_found", sandboxService.getSandboxStatus(baseSandbox.getSandboxId())); + Assertions.assertEquals("not_found", sandboxService.getSandboxStatus(browserSandbox.getSandboxId())); + Assertions.assertEquals("not_found", sandboxService.getSandboxStatus(fileSystemSandbox.getSandboxId())); + + System.out.println("All sandboxes successfully cleaned up"); + } + + /** + * Test getting all sandbox information + */ + @Test + void testGetAllSandboxes() { + System.out.println("Testing get all sandboxes..."); + + // Create some sandboxes + Sandbox baseSandbox = new BaseSandbox(sandboxService, testUserId, testSessionId); + Sandbox browserSandbox = new BrowserSandbox(sandboxService, testUserId, testSessionId); + + // Get all sandbox information + var allSandboxes = sandboxService.getAllSandboxes(); + Assertions.assertNotNull(allSandboxes, "Sandbox mapping should not be null"); + Assertions.assertTrue(allSandboxes.size() >= 2, "Should have at least 2 sandboxes"); + + System.out.println("Total sandboxes: " + allSandboxes.size()); + allSandboxes.forEach((key, model) -> System.out.println("Sandbox: " + key + " -> " + model.getContainerId())); + } + + /** + * Test error case: duplicate creation of same sandbox + */ + @Test + void testDuplicateSandboxCreation() { + System.out.println("Testing duplicate sandbox creation..."); + + // Create first sandbox + Sandbox sandbox1 = new BaseSandbox(sandboxService, testUserId, testSessionId); + Assertions.assertNotNull(sandbox1, "First sandbox should be created successfully"); + + // Try to create sandbox of same type (should return existing sandbox) + Sandbox sandbox2 = new BaseSandbox(sandboxService, testUserId, testSessionId); + Assertions.assertNotNull(sandbox2, "Should return existing sandbox"); + Assertions.assertEquals(sandbox1.getSandboxId(), sandbox2.getSandboxId(), "Duplicate creation should return the same sandbox instance"); + + System.out.println("Duplicate sandbox creation handled correctly"); + } +} diff --git a/sandbox-extensions/redis-extension/pom.xml b/sandbox-extensions/redis-extension/pom.xml new file mode 100644 index 00000000..a4d3621d --- /dev/null +++ b/sandbox-extensions/redis-extension/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + io.agentscope + agentscope-runtime + ${revision} + ../../pom.xml + + + redis-extension + + + 4.0.1 + 7.2.1.RELEASE + + + + + io.agentscope + agentscope-runtime-sandbox-core + ${revision} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.springframework.data + spring-data-redis + ${redis.client.version} + + + io.lettuce + lettuce-core + ${lettuce.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.testcontainers + junit-jupiter + test + + + + diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/RedisClientWrapper.java b/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisClientWrapper.java similarity index 98% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/util/RedisClientWrapper.java rename to sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisClientWrapper.java index 63d54118..b9b6c740 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/util/RedisClientWrapper.java +++ b/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisClientWrapper.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.util; +package io.agentscope.runtime.sandbox.map; -import io.agentscope.runtime.sandbox.manager.model.container.RedisManagerConfig; import io.lettuce.core.RedisClient; import io.lettuce.core.RedisURI; import io.lettuce.core.api.StatefulRedisConnection; diff --git a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/RedisManagerConfig.java b/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisManagerConfig.java similarity index 69% rename from core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/RedisManagerConfig.java rename to sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisManagerConfig.java index 8a266945..2599092e 100644 --- a/core/src/main/java/io/agentscope/runtime/sandbox/manager/model/container/RedisManagerConfig.java +++ b/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisManagerConfig.java @@ -13,19 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.agentscope.runtime.sandbox.manager.model.container; +package io.agentscope.runtime.sandbox.map; /** * Redis Manager Configuration Class */ public class RedisManagerConfig { - private String redisServer; - private Integer redisPort; - private Integer redisDb; - private String redisUser; - private String redisPassword; - private String redisPortKey; - private String redisContainerPoolKey; + private final String redisServer; + private final Integer redisPort; + private final Integer redisDb; + private final String redisUser; + private final String redisPassword; private RedisManagerConfig(Builder builder) { this.redisServer = builder.redisServer; @@ -33,8 +31,6 @@ private RedisManagerConfig(Builder builder) { this.redisDb = builder.redisDb; this.redisUser = builder.redisUser; this.redisPassword = builder.redisPassword; - this.redisPortKey = builder.redisPortKey; - this.redisContainerPoolKey = builder.redisContainerPoolKey; } public String getRedisServer() { @@ -57,14 +53,6 @@ public String getRedisPassword() { return redisPassword; } - public String getRedisPortKey() { - return redisPortKey; - } - - public String getRedisContainerPoolKey() { - return redisContainerPoolKey; - } - public static Builder builder() { return new Builder(); } @@ -75,8 +63,6 @@ public static class Builder { private Integer redisDb = 0; private String redisUser; private String redisPassword; - private String redisPortKey = "_runtime_sandbox_container_occupied_ports"; - private String redisContainerPoolKey = "_runtime_sandbox_container_container_pool"; public Builder redisServer(String redisServer) { this.redisServer = redisServer; @@ -103,16 +89,6 @@ public Builder redisPassword(String redisPassword) { return this; } - public Builder redisPortKey(String redisPortKey) { - this.redisPortKey = redisPortKey; - return this; - } - - public Builder redisContainerPoolKey(String redisContainerPoolKey) { - this.redisContainerPoolKey = redisContainerPoolKey; - return this; - } - public RedisManagerConfig build() { return new RedisManagerConfig(this); } diff --git a/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisSandboxMap.java b/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisSandboxMap.java new file mode 100644 index 00000000..b61204dd --- /dev/null +++ b/sandbox-extensions/redis-extension/src/main/java/io/agentscope/runtime/sandbox/map/RedisSandboxMap.java @@ -0,0 +1,221 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.agentscope.runtime.sandbox.map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import io.agentscope.runtime.sandbox.manager.model.container.SandboxKey; +import io.agentscope.runtime.sandbox.manager.utils.SandboxMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class RedisSandboxMap implements SandboxMap { + private static final Logger logger = LoggerFactory.getLogger(RedisSandboxMap.class); + + private final RedisClientWrapper redisClient; + private final ObjectMapper objectMapper; + + private static final String ID_TO_MODEL_PREFIX = "id_to_model:"; + private static final String KEY_TO_ID_PREFIX = "key_to_id:"; + private static final String ID_TO_KEY_PREFIX = "id_to_key:"; + + private static final String MAIN_DATA_PREFIX = "sandbox:"; + + public RedisSandboxMap(RedisManagerConfig redisManagerConfig) { + try { + this.redisClient = new RedisClientWrapper(redisManagerConfig); + String pong = this.redisClient.ping(); + logger.info("Redis connection test: {}", pong); + this.objectMapper = new ObjectMapper(); + } catch (Exception e) { + logger.error("Failed to initialize Redis client: {}", e.getMessage()); + throw new RuntimeException("Failed to initialize Redis", e); + } + } + + private String getKeyToIdKey(SandboxKey key) { + return MAIN_DATA_PREFIX + KEY_TO_ID_PREFIX + key.userID() + ":" + key.sessionID() + ":" + key.sandboxType(); + } + + private String getIdToKeyKey(String containerId) { + return MAIN_DATA_PREFIX + ID_TO_KEY_PREFIX + containerId; + } + + private String getIdToModelKey(String containerId) { + return MAIN_DATA_PREFIX + ID_TO_MODEL_PREFIX + containerId; + } + + @Override + public void addSandbox(SandboxKey sandboxKey, ContainerModel containerModel) { + if (sandboxKey == null || containerModel == null) { + logger.warn("Attempted to put null key or value, skipping"); + return; + } + + String containerId = containerModel.getContainerId(); + if (containerId == null || containerId.isEmpty()) { + throw new IllegalArgumentException("ContainerModel must have a non-empty sandboxId"); + } + + String keyToIdKey = getKeyToIdKey(sandboxKey); + String idToKeyKey = getIdToKeyKey(containerId); + String idToModelKey = getIdToModelKey(containerId); + + try { + String containerJson = objectMapper.writeValueAsString(containerModel); + String sandboxKeyJson = objectMapper.writeValueAsString(sandboxKey); + + redisClient.set(keyToIdKey, containerId); + redisClient.set(idToKeyKey, sandboxKeyJson); + redisClient.set(idToModelKey, containerJson); + + logger.info("Added container {} with key {} and id {}", + containerModel.getContainerName(), keyToIdKey, containerId); + } catch (JsonProcessingException e) { + logger.error("Failed to serialize during addSandbox: {}", e.getMessage()); + throw new RuntimeException("Failed to add sandbox to Redis", e); + } + } + + @Override + public ContainerModel getSandbox(SandboxKey sandboxKey) { + if (sandboxKey == null) return null; + String keyToIdKey = getKeyToIdKey(sandboxKey); + String containerId = redisClient.get(keyToIdKey); + if (containerId == null || containerId.isEmpty()) { + return null; + } + String idToModelKey = getIdToModelKey(containerId); + String json = redisClient.get(idToModelKey); + if (json == null || json.isEmpty()) { + return null; + } + try { + ContainerModel model = objectMapper.readValue(json, ContainerModel.class); + logger.debug("Retrieved container {} from Redis", model.getContainerName()); + return model; + } catch (JsonProcessingException e) { + logger.error("Failed to deserialize ContainerModel: {}", e.getMessage()); + throw new RuntimeException("Failed to deserialize container from Redis", e); + } + } + + @Override + public ContainerModel getSandbox(String containerId) { + if (containerId == null || containerId.isEmpty()) return null; + String idToModelKey = getIdToModelKey(containerId); + String json = redisClient.get(idToModelKey); + if (json == null || json.isEmpty()) { + return null; + } + try { + ContainerModel model = objectMapper.readValue(json, ContainerModel.class); + logger.debug("Retrieved container {} from Redis", model.getContainerName()); + return model; + } catch (JsonProcessingException e) { + logger.error("Failed to deserialize ContainerModel: {}", e.getMessage()); + throw new RuntimeException("Failed to deserialize container from Redis", e); + } + } + + @Override + public boolean removeSandbox(SandboxKey sandboxKey) { + if (sandboxKey == null) return false; + + String keyToIdKey = getKeyToIdKey(sandboxKey); + String containerId = redisClient.get(keyToIdKey); + if (containerId == null || containerId.isEmpty()) { + return false; + } + + redisClient.delete(keyToIdKey); + redisClient.delete(getIdToKeyKey(containerId)); + redisClient.delete(getIdToModelKey(containerId)); + + logger.info("Removed sandbox by key: {}, containerId: {}", sandboxKey, containerId); + return true; + } + + @Override + public void removeSandbox(String containerId) { + if (containerId == null || containerId.isEmpty()) return; + + String idToKeyKey = getIdToKeyKey(containerId); + String sandboxKeyJson = redisClient.get(idToKeyKey); + if (sandboxKeyJson != null && !sandboxKeyJson.isEmpty()) { + try { + SandboxKey key = objectMapper.readValue(sandboxKeyJson, SandboxKey.class); + String keyToIdKey = getKeyToIdKey(key); + redisClient.delete(keyToIdKey); + } catch (JsonProcessingException e) { + logger.warn("Failed to deserialize SandboxKey when removing sandbox by containerId: {}", e.getMessage()); + } + } + + redisClient.delete(idToKeyKey); + redisClient.delete(getIdToModelKey(containerId)); + + logger.info("Removed sandbox by containerId: {}", containerId); + } + + @Override + public boolean containSandbox(SandboxKey sandboxKey) { + if (sandboxKey == null) return false; + String keyToIdKey = getKeyToIdKey(sandboxKey); + String containerId = redisClient.get(keyToIdKey); + if (containerId == null || containerId.isEmpty()) { + return false; + } + return containSandbox(containerId); + } + + @Override + public boolean containSandbox(String containerId) { + if (containerId == null || containerId.isEmpty()) return false; + return redisClient.exists(getIdToModelKey(containerId)); + } + + @Override + public Map getAllSandboxes() { + Map result = new HashMap<>(); + + Set keys = redisClient.scan(MAIN_DATA_PREFIX + ID_TO_MODEL_PREFIX + "*"); + if (keys == null || keys.isEmpty()) { + return result; + } + + for (String fullKey : keys) { + String json = redisClient.get(fullKey); + if (json == null || json.isEmpty()) continue; + + try { + ContainerModel model = objectMapper.readValue(json, ContainerModel.class); + result.put(model.getContainerId(), model); + } catch (JsonProcessingException e) { + logger.error("Failed to deserialize ContainerModel for key {}: {}", fullKey, e.getMessage()); + } + } + + logger.debug("Retrieved {} sandboxes from Redis", result.size()); + return result; + } +} \ No newline at end of file diff --git a/sandbox-extensions/redis-extension/src/test/java/io/agentscope/runtime/sandbox/client/redis/RedisSandboxServiceTest.java b/sandbox-extensions/redis-extension/src/test/java/io/agentscope/runtime/sandbox/client/redis/RedisSandboxServiceTest.java new file mode 100644 index 00000000..8fdf283b --- /dev/null +++ b/sandbox-extensions/redis-extension/src/test/java/io/agentscope/runtime/sandbox/client/redis/RedisSandboxServiceTest.java @@ -0,0 +1,152 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.agentscope.runtime.sandbox.client.redis; + +import io.agentscope.runtime.sandbox.box.BaseSandbox; +import io.agentscope.runtime.sandbox.box.Sandbox; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.model.container.ContainerModel; +import io.agentscope.runtime.sandbox.manager.model.container.PortRange; +import io.agentscope.runtime.sandbox.map.RedisManagerConfig; +import io.agentscope.runtime.sandbox.map.RedisSandboxMap; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.condition.EnabledIf; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.util.Map; + +@EnabledIfDockerAvailable +@Testcontainers +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@EnabledIf(value = "isCI", disabledReason = "this test is designed to run only in the GitHub CI environment.") +public class RedisSandboxServiceTest { + + private static boolean isCI() { + return "true".equalsIgnoreCase(System.getProperty("CI", System.getenv("CI"))); + } + + private static final String TEST_USER_ID = "test_user"; + private static final String TEST_SESSION_ID = "test_session"; + + @Container + private static final GenericContainer redisContainer = new GenericContainer<>(DockerImageName.parse("valkey/valkey:8.1.2")).withExposedPorts(6379); // #gitleaks:allow + + /** + * Helper method to create RedisManagerConfig using the testcontainer's Redis instance + */ + private static RedisManagerConfig createRedisConfig(String portKey, String poolKey) { + return RedisManagerConfig.builder().build(); + } + + @Test + @Order(1) + public void testSandboxServiceWithRedis() { + RedisManagerConfig redisConfig = createRedisConfig("_test_runtime_sandbox_occupied_ports", "_test_runtime_sandbox_pool"); + ManagerConfig config = ManagerConfig.builder().containerPrefixKey("redis_test_").portRange(new PortRange(50000, 51000)).sandboxMap(new RedisSandboxMap(redisConfig)).build(); + try (SandboxService service = new SandboxService(config)) { + service.start(); + Assertions.assertNotNull(service, "SandboxService should be initialized"); + Sandbox sandbox = new BaseSandbox(service, TEST_USER_ID, TEST_SESSION_ID); + Assertions.assertNotNull(sandbox, "Container should be created"); + Map allSandboxes = service.getAllSandboxes(); + Assertions.assertEquals(1, allSandboxes.size(), "Should have 1 sandbox"); + service.stopAndRemoveSandbox(sandbox.getSandboxId()); + } catch (Exception e) { + Assertions.fail("Redis test failed: " + e.getMessage(), e); + } + } + + @Test + @Order(2) + public void testSandboxServiceWithoutRedis() { + ManagerConfig config = ManagerConfig.builder().containerPrefixKey("inmemory_test_").portRange(new PortRange(51000, 52000)).build(); + try (SandboxService service = new SandboxService(config)) { + service.start(); + Assertions.assertNotNull(service, "SandboxService should be initialized"); + Sandbox sandbox = new BaseSandbox(service, "memory_user", "memory_session"); + Assertions.assertNotNull(sandbox, "Container should be created"); + Assertions.assertNotNull(sandbox.getSandboxId(), "Container should have a ID"); + service.stopAndRemoveSandbox(sandbox.getSandboxId()); + } catch (Exception e) { + Assertions.fail("In-memory test failed: " + e.getMessage(), e); + } + } + + @Test + @Order(3) + public void testSharedStateWithRedis() { + RedisManagerConfig redisConfig = createRedisConfig("_shared_test_ports", "_shared_test_pool"); + ManagerConfig config = ManagerConfig.builder().containerPrefixKey("shared_test_").sandboxMap(new RedisSandboxMap(redisConfig)).portRange(new PortRange(52000, 53000)).build(); + try (SandboxService service1 = new SandboxService(config)) { + service1.start(); + Sandbox sandbox1 = new BaseSandbox(service1, TEST_USER_ID, TEST_SESSION_ID); + Assertions.assertNotNull(sandbox1, "Container should be created by service1"); + String containerId1 = sandbox1.getSandboxId(); + try (SandboxService service2 = new SandboxService(config)) { + service2.start(); + Sandbox sandbox2 = new BaseSandbox(service1, TEST_USER_ID, TEST_SESSION_ID); + Assertions.assertNotNull(sandbox2, "Container should be accessible from service2"); + Assertions.assertEquals(containerId1, sandbox2.getSandboxId(), "Both services should access the same container"); + service2.stopAndRemoveSandbox(containerId1); + } + } catch (Exception e) { + Assertions.fail("Shared state test failed: " + e.getMessage(), e); + } + } + + @Test + @Order(4) + public void testContainerPoolWithRedis() { + RedisManagerConfig redisConfig = createRedisConfig("_pool_test_ports", "_pool_test_queue"); + ManagerConfig config = ManagerConfig.builder().containerPrefixKey("pool_test_").sandboxMap(new RedisSandboxMap(redisConfig)).portRange(new PortRange(53000, 54000)).build(); + try (SandboxService service = new SandboxService(config)) { + service.start(); + Assertions.assertNotNull(service, "SandboxService should be initialized"); + Sandbox sandbox = new BaseSandbox(service, TEST_USER_ID, TEST_SESSION_ID); + Assertions.assertNotNull(sandbox, "Container should be created from pool"); + Assertions.assertNotNull(sandbox.getSandboxId(), "Container should have a name"); + service.stopSandbox(sandbox.getSandboxId()); + } catch (Exception e) { + Assertions.fail("Container pool test failed: " + e.getMessage(), e); + } + } + + @Test + @Order(5) + public void testContainerPersistenceInRedis() { + RedisManagerConfig redisConfig = createRedisConfig("_persist_test_ports", "_persist_test_pool"); + ManagerConfig config = ManagerConfig.builder().containerPrefixKey("persist_test_").sandboxMap(new RedisSandboxMap(redisConfig)).portRange(new PortRange(54000, 55000)).build(); + try (SandboxService service = new SandboxService(config)) { + service.start(); + Sandbox sandbox = new BaseSandbox(service, TEST_USER_ID, TEST_SESSION_ID); + Assertions.assertNotNull(sandbox.getSandboxId(), "Container should have a name"); + Map sandboxes = service.getAllSandboxes(); + Assertions.assertFalse(sandboxes.isEmpty(), "Should have at least one sandbox in Redis"); + service.stopAndRemoveSandbox(sandbox.getSandboxId()); + } + } + + @AfterEach + public void afterEach() throws InterruptedException { + Thread.sleep(1000); + } +} + diff --git a/web/pom.xml b/web/pom.xml index 0fc819b9..1b819a4f 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -50,7 +50,7 @@ io.agentscope - agentscope-runtime-core + agentscope-runtime-engine ${project.version} diff --git a/web/src/main/java/io/agentscope/runtime/app/AgentApp.java b/web/src/main/java/io/agentscope/runtime/app/AgentApp.java index 7ebd5880..4f5a45dd 100644 --- a/web/src/main/java/io/agentscope/runtime/app/AgentApp.java +++ b/web/src/main/java/io/agentscope/runtime/app/AgentApp.java @@ -41,10 +41,10 @@ import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; import io.agentscope.runtime.engine.services.memory.service.MemoryService; import io.agentscope.runtime.engine.services.memory.service.SessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; import io.agentscope.runtime.protocol.ProtocolConfig; -import io.agentscope.runtime.sandbox.manager.SandboxManager; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; import okio.Path; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -158,7 +158,7 @@ public AgentApp(String[] args) { StateService stateService = component(properties, STATE_SERVICE_PROVIDER, StateServiceProvider.class, new InMemoryStateService()); SessionHistoryService sessionHistoryService = component(properties, SESSION_HISTORY_SERVICE_PROVIDER, SessionHistoryServiceProvider.class, new InMemorySessionHistoryService()); MemoryService memoryService = component(properties, MEMORY_SERVICE_PROVIDER, MemoryServiceProvider.class, new InMemoryMemoryService()); - SandboxService sandboxService = component(properties, SANDBOX_SERVICE_PROVIDER, SandboxServiceProvider.class, new SandboxService(new SandboxManager())); + SandboxService sandboxService = component(properties, SANDBOX_SERVICE_PROVIDER, SandboxServiceProvider.class, new SandboxService(ManagerConfig.builder().build())); ServiceComponentManager serviceComponentManager = new ServiceComponentManager(); serviceComponentManager.setStateService(stateService); serviceComponentManager.setSessionHistoryService(sessionHistoryService); diff --git a/web/src/main/java/io/agentscope/runtime/autoconfigure/controller/SandboxManagerController.java b/web/src/main/java/io/agentscope/runtime/autoconfigure/controller/SandboxServiceController.java similarity index 85% rename from web/src/main/java/io/agentscope/runtime/autoconfigure/controller/SandboxManagerController.java rename to web/src/main/java/io/agentscope/runtime/autoconfigure/controller/SandboxServiceController.java index 94d06cc3..8f92d9dd 100644 --- a/web/src/main/java/io/agentscope/runtime/autoconfigure/controller/SandboxManagerController.java +++ b/web/src/main/java/io/agentscope/runtime/autoconfigure/controller/SandboxServiceController.java @@ -16,37 +16,29 @@ package io.agentscope.runtime.autoconfigure.controller; +import com.fasterxml.jackson.databind.ObjectMapper; import io.agentscope.runtime.engine.Runner; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.remote.RemoteWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import javax.annotation.PostConstruct; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.HashMap; import java.util.Map; -import javax.annotation.PostConstruct; - -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.remote.RemoteWrapper; - @RestController @RequestMapping("/sandbox") -public class SandboxManagerController { +public class SandboxServiceController { - private static final Logger logger = LoggerFactory.getLogger(SandboxManagerController.class); + private static final Logger logger = LoggerFactory.getLogger(SandboxServiceController.class); private final Map endpointRegistry = new HashMap<>(); @@ -55,11 +47,11 @@ public class SandboxManagerController { @PostConstruct public void registerEndpoints() { - Class clazz = SandboxManager.class; + Class clazz = SandboxService.class; logger.info("Scanning class: {}", clazz.getName()); Method[] methods = clazz.getDeclaredMethods(); - logger.info("Found {} methods in SandboxManager", methods.length); + logger.info("Found {} methods in SandboxService", methods.length); for (Method method : methods) { RemoteWrapper annotation = method.getAnnotation(RemoteWrapper.class); @@ -74,7 +66,7 @@ public void registerEndpoints() { MethodInfo methodInfo = new MethodInfo(method, annotation); endpointRegistry.put(path, methodInfo); - logger.info("Registered endpoint: {} {} -> {}", annotation.method(), path, method.getName()); + logger.info("Registered endpoint: {} /sandbox{} -> {}", annotation.method(), path, method.getName()); } } @@ -121,7 +113,7 @@ private String extractPath(jakarta.servlet.http.HttpServletRequest request) { String requestUri = request.getRequestURI(); String contextPath = request.getContextPath(); String path = requestUri.substring(contextPath.length()); - + // Remove /sandbox prefix to match registered endpoints if (path.startsWith("/sandbox")) { path = path.substring("/sandbox".length()); @@ -129,7 +121,7 @@ private String extractPath(jakarta.servlet.http.HttpServletRequest request) { path = "/"; } } - + return path; } @@ -139,9 +131,10 @@ private ResponseEntity> handleRequest( RequestMethod httpMethod) { // Todo: fix this - SandboxManager sandboxManager = runner.getSandboxManager(); + SandboxService sandboxService = runner.getSandboxService(); logger.info("Handling {} request for path: {}", httpMethod, path); + System.out.println("Handling " + httpMethod + " request for path: " + path); MethodInfo methodInfo = endpointRegistry.get(path); @@ -162,7 +155,11 @@ private ResponseEntity> handleRequest( try { Object[] args = prepareArguments(methodInfo.method, requestData); - Object result = methodInfo.method.invoke(sandboxManager, args); + ObjectMapper mapper = new ObjectMapper(); + String sandboxJson = mapper.writeValueAsString(args[0]); + Map request = Map.of("sandbox", sandboxJson); + + Object result = methodInfo.method.invoke(sandboxService, args); Map response = new HashMap<>(); String successKey = methodInfo.annotation.successKey(); @@ -261,7 +258,20 @@ private Object convertValue(Object value, Class targetType) { return value; } - return value; + if (value instanceof String strValue) { + String trimmed = strValue.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[") && !targetType.isPrimitive()) { + try { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(strValue, targetType); + } catch (Exception e) { + logger.warn("Failed to parse JSON string to {}: {}", targetType.getName(), e.getMessage()); + } + } + } + + ObjectMapper mapper = new ObjectMapper(); + return mapper.convertValue(value, targetType); } private Object getDefaultValue(Class type) { diff --git a/web/src/test/java/io/agentscope/runtime/adapters/agentscope/MyAgentScopeAgentHandler.java b/web/src/test/java/io/agentscope/runtime/adapters/agentscope/MyAgentScopeAgentHandler.java index 2bb75900..11a47c9d 100644 --- a/web/src/test/java/io/agentscope/runtime/adapters/agentscope/MyAgentScopeAgentHandler.java +++ b/web/src/test/java/io/agentscope/runtime/adapters/agentscope/MyAgentScopeAgentHandler.java @@ -81,7 +81,7 @@ public Flux streamQuery(AgentRequest request, Ob if (sandboxService != null) { try { // Create a BaseSandbox instance for this session - Sandbox sandbox = sandboxService.connect(userId, sessionId, BaseSandbox.class); + Sandbox sandbox = new BaseSandbox(sandboxService, userId, sessionId); // Register Python code execution tool (matching Python: execute_python_code) toolkit.registerTool(ToolkitInit.RunPythonCodeTool(sandbox)); diff --git a/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeAgentDeployExample.java b/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeAgentDeployExample.java index b83f5ed1..5329a8f9 100644 --- a/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeAgentDeployExample.java +++ b/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeAgentDeployExample.java @@ -21,11 +21,10 @@ import io.agentscope.runtime.engine.services.agent_state.InMemoryStateService; import io.agentscope.runtime.engine.services.memory.persistence.memory.service.InMemoryMemoryService; import io.agentscope.runtime.engine.services.memory.persistence.session.InMemorySessionHistoryService; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.jetbrains.annotations.NotNull; /** @@ -60,13 +59,11 @@ public static void basicExample() { @NotNull private static SandboxService buidSandboxService() { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - SandboxService sandboxService = new SandboxService( - new SandboxManager(managerConfig) - ); + SandboxService sandboxService = new SandboxService(managerConfig); return sandboxService; } diff --git a/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeDeployWithCommandLineTests.java b/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeDeployWithCommandLineTests.java index f8c48a1f..dbda0c87 100644 --- a/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeDeployWithCommandLineTests.java +++ b/web/src/test/java/io/agentscope/runtime/deployer/AgentScopeDeployWithCommandLineTests.java @@ -23,11 +23,10 @@ import io.agentscope.runtime.adapters.AgentHandler; import io.agentscope.runtime.adapters.agentscope.MyAgentScopeAgentHandler; import io.agentscope.runtime.app.AgentApp; -import io.agentscope.runtime.engine.services.sandbox.SandboxService; -import io.agentscope.runtime.sandbox.manager.SandboxManager; -import io.agentscope.runtime.sandbox.manager.client.config.BaseClientConfig; -import io.agentscope.runtime.sandbox.manager.client.config.KubernetesClientConfig; -import io.agentscope.runtime.sandbox.manager.model.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.ManagerConfig; +import io.agentscope.runtime.sandbox.manager.SandboxService; +import io.agentscope.runtime.sandbox.manager.client.container.BaseClientStarter; +import io.agentscope.runtime.sandbox.manager.client.container.docker.DockerClientStarter; import org.junit.jupiter.api.Test; public class AgentScopeDeployWithCommandLineTests { @@ -68,12 +67,11 @@ public AgentHandler get(Properties properties, AgentApp.ServiceComponentManager public static class MySandboxServiceProvider implements AgentApp.SandboxServiceProvider { @Override public SandboxService get(Properties properties) { - BaseClientConfig clientConfig = KubernetesClientConfig.builder().build(); + BaseClientStarter clientConfig = DockerClientStarter.builder().build(); ManagerConfig managerConfig = ManagerConfig.builder() - .containerDeployment(clientConfig) + .clientStarter(clientConfig) .build(); - return new SandboxService( - new SandboxManager(managerConfig)); + return new SandboxService(managerConfig); } } }