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 extends Sandbox> 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 extends Sandbox> 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 extends Sandbox> 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}