From acc576203c65f4fefeeaedca87abb1d6497f0fa9 Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 01/10] feat(sdk): add speedup method(apt, github, pip) --- rock/sdk/sandbox/client.py | 124 ++++++++++++++ rock/sdk/sandbox/speedup/__init__.py | 6 + rock/sdk/sandbox/speedup/base.py | 55 +++++++ rock/sdk/sandbox/speedup/constants.py | 152 ++++++++++++++++++ rock/sdk/sandbox/speedup/executor.py | 129 +++++++++++++++ .../sandbox/speedup/strategies/__init__.py | 7 + rock/sdk/sandbox/speedup/strategies/apt.py | 58 +++++++ rock/sdk/sandbox/speedup/strategies/github.py | 72 +++++++++ rock/sdk/sandbox/speedup/strategies/pip.py | 69 ++++++++ rock/sdk/sandbox/speedup/types.py | 11 ++ tests/integration/sdk/sandbox/test_speedup.py | 100 ++++++++++++ tests/unit/sdk/test_speedup.py | 81 ++++++++++ 12 files changed, 864 insertions(+) create mode 100644 rock/sdk/sandbox/speedup/__init__.py create mode 100644 rock/sdk/sandbox/speedup/base.py create mode 100644 rock/sdk/sandbox/speedup/constants.py create mode 100644 rock/sdk/sandbox/speedup/executor.py create mode 100644 rock/sdk/sandbox/speedup/strategies/__init__.py create mode 100644 rock/sdk/sandbox/speedup/strategies/apt.py create mode 100644 rock/sdk/sandbox/speedup/strategies/github.py create mode 100644 rock/sdk/sandbox/speedup/strategies/pip.py create mode 100644 rock/sdk/sandbox/speedup/types.py create mode 100644 tests/integration/sdk/sandbox/test_speedup.py create mode 100644 tests/unit/sdk/test_speedup.py diff --git a/rock/sdk/sandbox/client.py b/rock/sdk/sandbox/client.py index e67ea51bd..88b8f23f9 100644 --- a/rock/sdk/sandbox/client.py +++ b/rock/sdk/sandbox/client.py @@ -43,6 +43,7 @@ from rock.sdk.sandbox.config import SandboxConfig, SandboxGroupConfig from rock.sdk.sandbox.model_service.base import ModelService from rock.sdk.sandbox.remote_user import LinuxRemoteUser, RemoteUser +from rock.sdk.sandbox.speedup import SpeedupExecutor, SpeedupType from rock.utils import HttpUtils, extract_nohup_pid, retry_async logger = logging.getLogger(__name__) @@ -735,6 +736,129 @@ async def _read_lines(start_line: int, end_line: int) -> str: result = ReadFileResponse(content=result) return result + async def execute_script( + self, + script_content: str, + script_name: str = None, + wait_timeout: int = 300, + wait_interval: int = 10, + cleanup: bool = True, + ) -> Observation: + """ + Execute a script in the sandbox. + + This is a general-purpose method that: + 1. Uploads the script to /tmp + 2. Executes it using nohup mode + 3. Optionally cleans up the script file + + Args: + script_content: The script content to execute + script_name: Optional custom script name. If None, generates timestamp-based name + wait_timeout: Maximum time to wait for script completion (seconds) + wait_interval: Interval between process checks (seconds) + cleanup: Whether to delete the script file after execution + + Returns: + Observation: Execution result + + Examples: + # Execute a simple script + result = await sandbox.execute_script( + script_content="#!/bin/bash\\necho 'Hello World'", + wait_timeout=60 + ) + + # Execute with custom name and keep the script + result = await sandbox.execute_script( + script_content=my_script, + script_name="my_custom_script.sh", + cleanup=False + ) + """ + # Generate script path + if script_name is None: + timestamp = str(time.time_ns()) + script_name = f"script_{timestamp}.sh" + + script_path = f"/tmp/{script_name}" + + try: + # Upload script + logger.info(f"Uploading script to {script_path}") + write_result = await self.write_file_by_path(script_content, script_path) + + if not write_result.success: + error_msg = f"Failed to upload script: {write_result.message}" + logger.error(error_msg) + return Observation(output=error_msg, exit_code=1, failure_reason="Script upload failed") + + # Execute script + logger.info(f"Executing script: {script_path} (timeout={wait_timeout}s)") + result = await self.arun( + cmd=f"bash {script_path}", + mode=RunMode.NOHUP, + wait_timeout=wait_timeout, + wait_interval=wait_interval, + ) + + return result + + except Exception as e: + error_msg = f"Script execution failed: {str(e)}" + logger.error(error_msg) + return Observation(output=error_msg, exit_code=1, failure_reason=error_msg) + + finally: + # Cleanup script if requested + if cleanup: + try: + logger.info(f"Cleaning up script: {script_path}") + await self.execute(Command(command=["rm", "-f", script_path])) + logger.debug(f"Script cleaned up successfully: {script_path}") + except Exception as e: + logger.warning(f"Failed to cleanup script {script_path}: {e}") + + async def speedup(self, speedup_type: SpeedupType, speedup_value: str, timeout: int = 300) -> Observation: + """ + Configure acceleration for package managers or network resources + + Args: + speedup_type: Type of speedup configuration (SpeedupType.APT, SpeedupType.PIP, SpeedupType.GITHUB) + speedup_value: Speedup value, format depends on speedup_type: + - APT: Mirror URL with protocol + Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" + - PIP: Mirror URL with protocol + Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" + - GITHUB: IP address for github.com + Examples: "20.205.243.166" + timeout: Execution timeout in seconds, default 300 + + Returns: + Observation: Execution result containing output and exit code + + Examples: + # Configure APT mirror + result = await sandbox.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com" + ) + + # Configure PIP mirror with custom path + result = await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com" + ) + + # Configure GitHub acceleration + result = await sandbox.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="20.205.243.166" + ) + """ + executor = SpeedupExecutor(self) + return await executor.execute(speedup_type, speedup_value, timeout) + async def _generate_tmp_session_name(self) -> str: timestamp = str(time.time_ns()) return f"bash-{timestamp}" diff --git a/rock/sdk/sandbox/speedup/__init__.py b/rock/sdk/sandbox/speedup/__init__.py new file mode 100644 index 000000000..d65a79560 --- /dev/null +++ b/rock/sdk/sandbox/speedup/__init__.py @@ -0,0 +1,6 @@ +"""Speedup module for sandbox acceleration.""" + +from rock.sdk.sandbox.speedup.executor import SpeedupExecutor +from rock.sdk.sandbox.speedup.types import SpeedupType + +__all__ = ["SpeedupExecutor", "SpeedupType"] diff --git a/rock/sdk/sandbox/speedup/base.py b/rock/sdk/sandbox/speedup/base.py new file mode 100644 index 000000000..8d09ec75d --- /dev/null +++ b/rock/sdk/sandbox/speedup/base.py @@ -0,0 +1,55 @@ +"""Abstract base class for speedup strategies.""" + +from abc import ABC, abstractmethod + + +class SpeedupStrategy(ABC): + """Speedup strategy abstract base class""" + + @abstractmethod + async def precheck(self, sandbox) -> tuple[bool, str]: + """ + Precheck if environment meets requirements + + Args: + sandbox: Sandbox instance + + Returns: + tuple[bool, str]: (check passed, check message) + """ + pass + + @abstractmethod + def generate_script(self, speedup_value: str) -> str: + """ + Generate speedup configuration script + + Args: + speedup_value: Speedup value (mirror URL, IP address, etc.) + + Returns: + str: Script content + """ + pass + + @abstractmethod + def parse_value(self, speedup_value: str) -> dict[str, str]: + """ + Parse speedup value and extract required parameters + + Args: + speedup_value: Speedup value string + + Returns: + dict[str, str]: Parameters for template filling + """ + pass + + def get_nohup_wait_timeout(self) -> int: + """ + Get nohup wait timeout (seconds) + + Returns: + int: Timeout value + """ + return 300 diff --git a/rock/sdk/sandbox/speedup/constants.py b/rock/sdk/sandbox/speedup/constants.py new file mode 100644 index 000000000..a66f0e386 --- /dev/null +++ b/rock/sdk/sandbox/speedup/constants.py @@ -0,0 +1,152 @@ +"""Script templates and constants for speedup.""" + +# APT speedup configuration script template (unified) +setup_apt_source_template = """#!/bin/bash + +detect_system_and_version() {{ + if [ -f /etc/debian_version ]; then + . /etc/os-release + if [ "$ID" = "ubuntu" ]; then + echo "ubuntu:$VERSION_CODENAME" + elif [ "$ID" = "debian" ]; then + echo "debian:$VERSION_CODENAME" + else + echo "unknown:" + fi + else + echo "unknown:" + fi +}} + +setup_apt_source() {{ + SYSTEM_INFO=$(detect_system_and_version) + SYSTEM=$(echo "$SYSTEM_INFO" | cut -d: -f1) + CODENAME=$(echo "$SYSTEM_INFO" | cut -d: -f2) + echo "System type: $SYSTEM, Version codename: $CODENAME" + + # Backup original sources file + if [ ! -f /etc/apt/sources.list.backup ]; then + cp /etc/apt/sources.list /etc/apt/sources.list.backup + fi + + if [ "$SYSTEM" = "debian" ]; then + if [ -z "$CODENAME" ]; then + CODENAME="bookworm" + fi + cat > /etc/apt/sources.list < /etc/apt/sources.list < /etc/apt/apt.conf.d/99speedup <>> APT source configuration completed" +}} + +setup_apt_source +""" + +# PIP speedup configuration script template +setup_pip_source_template = """#!/bin/bash + +setup_pip_source() {{ + echo ">>> Configuring pip source..." + + # Configure for root user + mkdir -p /root/.pip + cat > /root/.pip/pip.conf < "$home_dir/.pip/pip.conf" </dev/null || true + fi + done + + echo ">>> pip source configuration completed" +}} + +setup_pip_source +""" + +# GitHub hosts speedup configuration script template (github.com only) +setup_github_hosts_template = """#!/bin/bash + +setup_github_hosts() {{ + echo ">>> Configuring GitHub hosts for github.com acceleration..." + + # Backup original hosts file if not already backed up + if [ ! -f /etc/hosts.backup ]; then + cp /etc/hosts /etc/hosts.backup + echo "Hosts file backed up to /etc/hosts.backup" + fi + + # Remove existing github.com entry if any + sed -i '/github\\.com$/d' /etc/hosts + + # Add new github.com hosts entry + echo "{hosts_entry}" | tee -a /etc/hosts + + echo ">>> GitHub hosts configuration completed" + echo "Current github.com entry in /etc/hosts:" + grep 'github\\.com$' /etc/hosts || echo "No github.com entry found" +}} + +setup_github_hosts +""" diff --git a/rock/sdk/sandbox/speedup/executor.py b/rock/sdk/sandbox/speedup/executor.py new file mode 100644 index 000000000..7b7b51671 --- /dev/null +++ b/rock/sdk/sandbox/speedup/executor.py @@ -0,0 +1,129 @@ +"""Speedup executor for coordinating speedup operations.""" + +from __future__ import annotations # Postpone annotation evaluation to avoid circular imports. + +import logging +from typing import TYPE_CHECKING + +from rock.actions import Observation +from rock.sdk.sandbox.speedup.base import SpeedupStrategy +from rock.sdk.sandbox.speedup.strategies.apt import AptSpeedupStrategy +from rock.sdk.sandbox.speedup.strategies.github import GithubSpeedupStrategy +from rock.sdk.sandbox.speedup.strategies.pip import PipSpeedupStrategy +from rock.sdk.sandbox.speedup.types import SpeedupType + +if TYPE_CHECKING: + from rock.sdk.sandbox.client import Sandbox + +logger = logging.getLogger(__name__) + + +class SpeedupExecutor: + """Speedup executor (coordinator)""" + + # Strategy registry + _strategies: dict[SpeedupType, type[SpeedupStrategy]] = { + SpeedupType.APT: AptSpeedupStrategy, + SpeedupType.PIP: PipSpeedupStrategy, + SpeedupType.GITHUB: GithubSpeedupStrategy, + } + + def __init__(self, sandbox: Sandbox): + """ + Initialize executor + + Args: + sandbox: Sandbox instance + """ + self.sandbox = sandbox + + @classmethod + def register_strategy(cls, speedup_type: SpeedupType, strategy_class: type[SpeedupStrategy]): + """ + Register a new speedup strategy + + Args: + speedup_type: Speedup type + strategy_class: Strategy class + """ + cls._strategies[speedup_type] = strategy_class + logger.info(f"Registered speedup strategy: {speedup_type} -> {strategy_class.__name__}") + + async def execute(self, speedup_type: SpeedupType, speedup_value: str, timeout: int = 300) -> Observation: + """ + Execute speedup configuration (template method pattern) + + Args: + speedup_type: Speedup type (APT, PIP, GITHUB, etc.) + speedup_value: Speedup value string (mirror URL, IP address, etc.) + timeout: Timeout in seconds + + Returns: + Observation: Execution result + """ + logger.info(f"Starting speedup: type={speedup_type}, value={speedup_value}, timeout={timeout}") + + # 1. Get strategy + strategy = self._get_strategy(speedup_type) + if not strategy: + error_msg = f"Unsupported speedup type: {speedup_type}" + logger.error(error_msg) + return Observation(output=error_msg, exit_code=1, failure_reason="Invalid speedup type") + + # 2. Precheck environment + precheck_success, precheck_msg = await self._precheck(strategy) + if not precheck_success: + logger.warning(f"Precheck failed: {precheck_msg}") + return Observation(output=precheck_msg, exit_code=1, failure_reason="Precheck failed") + + logger.info(f"Precheck passed: {precheck_msg}") + + # 3. Generate script + script_content = self._generate_script(strategy, speedup_value) + if not script_content: + error_msg = "Failed to generate speedup script" + logger.error(error_msg) + return Observation(output=error_msg, exit_code=1, failure_reason="Script generation failed") + + # 4. Execute script using the general execute_script method + result = await self.sandbox.execute_script( + script_content=script_content, + wait_timeout=strategy.get_nohup_wait_timeout(), + cleanup=True, + ) + + # 5. Log result + if result.exit_code == 0: + logger.info(f"Speedup completed successfully: type={speedup_type}, output_length={len(result.output)}") + else: + logger.error( + f"Speedup failed: type={speedup_type}, exit_code={result.exit_code}, " + f"failure_reason={result.failure_reason}" + ) + + return result + + def _get_strategy(self, speedup_type: SpeedupType) -> SpeedupStrategy | None: + """Get strategy instance""" + strategy_class = self._strategies.get(speedup_type) + if not strategy_class: + return None + return strategy_class() + + async def _precheck(self, strategy: SpeedupStrategy) -> tuple[bool, str]: + """Execute precheck""" + try: + logger.debug("Running precheck...") + return await strategy.precheck(self.sandbox) + except Exception as e: + logger.error(f"Precheck exception: {e}") + return False, f"Precheck failed with exception: {str(e)}" + + def _generate_script(self, strategy: SpeedupStrategy, speedup_value: str) -> str | None: + """Generate script content""" + try: + logger.debug(f"Generating script for value: {speedup_value}") + return strategy.generate_script(speedup_value) + except Exception as e: + logger.error(f"Script generation exception: {e}") + return None diff --git a/rock/sdk/sandbox/speedup/strategies/__init__.py b/rock/sdk/sandbox/speedup/strategies/__init__.py new file mode 100644 index 000000000..cef14b067 --- /dev/null +++ b/rock/sdk/sandbox/speedup/strategies/__init__.py @@ -0,0 +1,7 @@ +"""Speedup strategies.""" + +from rock.sdk.sandbox.speedup.strategies.apt import AptSpeedupStrategy +from rock.sdk.sandbox.speedup.strategies.github import GithubSpeedupStrategy +from rock.sdk.sandbox.speedup.strategies.pip import PipSpeedupStrategy + +__all__ = ["AptSpeedupStrategy", "PipSpeedupStrategy", "GithubSpeedupStrategy"] diff --git a/rock/sdk/sandbox/speedup/strategies/apt.py b/rock/sdk/sandbox/speedup/strategies/apt.py new file mode 100644 index 000000000..6d09563fb --- /dev/null +++ b/rock/sdk/sandbox/speedup/strategies/apt.py @@ -0,0 +1,58 @@ +"""APT speedup strategy implementation.""" + +import logging + +from rock.actions import Command +from rock.sdk.sandbox.speedup.base import SpeedupStrategy +from rock.sdk.sandbox.speedup.constants import setup_apt_source_template + +logger = logging.getLogger(__name__) + + +class AptSpeedupStrategy(SpeedupStrategy): + """APT speedup strategy""" + + async def precheck(self, sandbox) -> tuple[bool, str]: + """Check if the system is Debian/Ubuntu based""" + try: + # Use execute instead of arun for simple checks + result = await sandbox.execute(Command(command=["test", "-f", "/etc/debian_version"])) + if result.exit_code == 0: + logger.info("APT precheck passed: Debian/Ubuntu system detected") + return True, "System check passed: Debian/Ubuntu detected" + else: + logger.warning("APT precheck failed: Not a Debian/Ubuntu system") + return False, "This is not a Debian/Ubuntu system, APT speedup is not supported" + except Exception as e: + logger.error(f"APT precheck failed with exception: {e}") + return False, f"System check failed: {str(e)}" + + def parse_value(self, speedup_value: str) -> dict[str, str]: + """ + Parse APT mirror URL + + Args: + speedup_value: Mirror URL with protocol + + Examples: + http://mirrors.cloud.aliyuncs.com -> { + "mirror_base": "http://mirrors.cloud.aliyuncs.com" + } + https://mirrors.aliyun.com/ -> { + "mirror_base": "https://mirrors.aliyun.com" + } + """ + # Remove trailing slash + mirror_base = speedup_value.rstrip("/") + + return {"mirror_base": mirror_base} + + def generate_script(self, speedup_value: str) -> str: + """Generate APT speedup script""" + params = self.parse_value(speedup_value) + logger.info(f"Generating APT speedup script with mirror: {params['mirror_base']}") + return setup_apt_source_template.format(**params) + + def get_nohup_wait_timeout(self) -> int: + """APT update may take longer time""" + return 300 diff --git a/rock/sdk/sandbox/speedup/strategies/github.py b/rock/sdk/sandbox/speedup/strategies/github.py new file mode 100644 index 000000000..fc01f4c44 --- /dev/null +++ b/rock/sdk/sandbox/speedup/strategies/github.py @@ -0,0 +1,72 @@ +"""GitHub speedup strategy implementation.""" + +import logging +import re + +from rock.actions import Command +from rock.sdk.sandbox.speedup.base import SpeedupStrategy +from rock.sdk.sandbox.speedup.constants import setup_github_hosts_template + +logger = logging.getLogger(__name__) + + +class GithubSpeedupStrategy(SpeedupStrategy): + """GitHub speedup strategy for github.com acceleration""" + + async def precheck(self, sandbox) -> tuple[bool, str]: + """Check if /etc/hosts is writable""" + try: + # Check if /etc/hosts exists and is writable + result = await sandbox.execute(Command(command=["test", "-w", "/etc/hosts"])) + if result.exit_code == 0: + logger.info("GitHub precheck passed: /etc/hosts is writable") + return True, "System check passed: /etc/hosts is writable" + else: + logger.warning("GitHub precheck failed: /etc/hosts is not writable") + return False, "/etc/hosts is not writable, GitHub speedup requires root privileges" + except Exception as e: + logger.error(f"GitHub precheck failed with exception: {e}") + return False, f"System check failed: {str(e)}" + + def parse_value(self, speedup_value: str) -> dict[str, str]: + """ + Parse GitHub IP address for github.com acceleration + + Args: + speedup_value: IP address for github.com + + Examples: + "20.205.243.166" -> { + "hosts_entry": "20.205.243.166 github.com" + } + """ + # Trim whitespace + ip_address = speedup_value.strip() + + # Validate IP address format + ip_pattern = r"^(\d{1,3}\.){3}\d{1,3}$" + if not re.match(ip_pattern, ip_address): + logger.warning(f"Invalid IP address format: {ip_address}") + raise ValueError(f"Invalid IP address format: {ip_address}. Expected format: x.x.x.x") + + # Validate IP address range (0-255 for each octet) + octets = ip_address.split(".") + for octet in octets: + if int(octet) > 255: + logger.warning(f"Invalid IP address: {ip_address}, octet value exceeds 255") + raise ValueError(f"Invalid IP address: {ip_address}, octet value must be 0-255") + + # Build hosts entry for github.com + hosts_entry = f"{ip_address} github.com" + + return {"hosts_entry": hosts_entry} + + def generate_script(self, speedup_value: str) -> str: + """Generate GitHub hosts speedup script""" + params = self.parse_value(speedup_value) + logger.info(f"Generating GitHub speedup script with hosts entry: {params['hosts_entry']}") + return setup_github_hosts_template.format(**params) + + def get_nohup_wait_timeout(self) -> int: + """GitHub hosts configuration is very fast""" + return 30 diff --git a/rock/sdk/sandbox/speedup/strategies/pip.py b/rock/sdk/sandbox/speedup/strategies/pip.py new file mode 100644 index 000000000..827e5f6cc --- /dev/null +++ b/rock/sdk/sandbox/speedup/strategies/pip.py @@ -0,0 +1,69 @@ +"""PIP speedup strategy implementation.""" + +import logging +from urllib.parse import urlparse + +from rock.actions import Command +from rock.sdk.sandbox.speedup.base import SpeedupStrategy +from rock.sdk.sandbox.speedup.constants import setup_pip_source_template + +logger = logging.getLogger(__name__) + + +class PipSpeedupStrategy(SpeedupStrategy): + """PIP speedup strategy""" + + async def precheck(self, sandbox) -> tuple[bool, str]: + """Check if pip is installed""" + try: + # Try pip3 first, then pip + result = await sandbox.execute(Command(command=["sh", "-c", "pip3 --version 2>&1 || pip --version 2>&1"])) + if result.exit_code == 0: + pip_version = result.stdout.strip() + logger.info(f"PIP precheck passed: {pip_version}") + return True, f"PIP check passed: {pip_version}" + else: + logger.warning("PIP precheck failed: pip not found") + return False, "pip is not installed, PIP speedup is not supported" + except Exception as e: + logger.error(f"PIP precheck failed with exception: {e}") + return False, f"PIP check failed: {str(e)}" + + def parse_value(self, speedup_value: str) -> dict[str, str]: + """ + Parse PIP mirror URL + + Args: + speedup_value: Mirror URL with protocol + + Examples: + http://mirrors.cloud.aliyuncs.com -> { + "pip_index_url": "http://mirrors.cloud.aliyuncs.com/pypi/simple/", + "pip_trusted_host": "mirrors.cloud.aliyuncs.com" + } + https://mirrors.aliyun.com -> { + "pip_index_url": "https://mirrors.aliyun.com/pypi/simple/", + "pip_trusted_host": "mirrors.aliyun.com" + } + """ + # Remove trailing slash + base_url = speedup_value.rstrip("/") + + # Extract trusted host from URL + parsed = urlparse(base_url) + trusted_host = parsed.netloc + + # Build index URL by appending /pypi/simple/ + index_url = f"{base_url}/pypi/simple/" + + return {"pip_index_url": index_url, "pip_trusted_host": trusted_host} + + def generate_script(self, speedup_value: str) -> str: + """Generate PIP speedup script""" + params = self.parse_value(speedup_value) + logger.info(f"Generating PIP speedup script with mirror: {params['pip_index_url']}") + return setup_pip_source_template.format(**params) + + def get_nohup_wait_timeout(self) -> int: + """PIP configuration is usually fast""" + return 60 diff --git a/rock/sdk/sandbox/speedup/types.py b/rock/sdk/sandbox/speedup/types.py new file mode 100644 index 000000000..0bcab3361 --- /dev/null +++ b/rock/sdk/sandbox/speedup/types.py @@ -0,0 +1,11 @@ +"""Speedup types definition.""" + +from enum import Enum + + +class SpeedupType(str, Enum): + """Speedup type enumeration""" + + APT = "apt" + PIP = "pip" + GITHUB = "github" diff --git a/tests/integration/sdk/sandbox/test_speedup.py b/tests/integration/sdk/sandbox/test_speedup.py new file mode 100644 index 000000000..1453a9d97 --- /dev/null +++ b/tests/integration/sdk/sandbox/test_speedup.py @@ -0,0 +1,100 @@ +"""Tests for sandbox speedup functionality.""" + +import pytest + +from rock.actions import Command +from rock.logger import init_logger +from rock.sdk.sandbox.client import Sandbox +from rock.sdk.sandbox.speedup import SpeedupType +from tests.integration.conftest import SKIP_IF_NO_DOCKER + +logger = init_logger(__name__) + + +async def _assert_speedup_apt(sandbox: Sandbox): + logger.info("Testing APT public mirror configuration...") + result = await sandbox.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com", + ) + assert result.exit_code == 0, f"APT public mirror failed: {result.output}" + logger.info("APT public mirror configured successfully") + + logger.info("Verifying /etc/apt/sources.list content...") + check_result = await sandbox.execute(Command(command=["cat", "/etc/apt/sources.list"])) + assert check_result.exit_code == 0, "Failed to read /etc/apt/sources.list" + sources_content = check_result.stdout + + assert ( + "mirrors.cloud.aliyuncs.com" in sources_content + ), f"Mirror URL not found in sources.list. Content:\n{sources_content}" + assert ( + "deb http://mirrors.cloud.aliyuncs.com" in sources_content + ), f"Expected deb entry not found. Content:\n{sources_content}" + logger.info(f"APT sources.list verified successfully:\n{sources_content}") + + +async def _assert_speedup_pip(sandbox: Sandbox): + logger.info("Testing PIP mirror (http)...") + result = await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="http://mirrors.cloud.aliyuncs.com", + ) + assert result.exit_code == 0, f"PIP mirror failed: {result.output}" + logger.info("PIP mirror configured successfully") + + logger.info("Verifying /root/.pip/pip.conf content...") + check_result = await sandbox.execute(Command(command=["cat", "/root/.pip/pip.conf"])) + assert check_result.exit_code == 0, "Failed to read /root/.pip/pip.conf" + pip_config_content = check_result.stdout + + assert ( + "mirrors.cloud.aliyuncs.com/pypi/simple/" in pip_config_content + ), f"PIP mirror URL not found in pip.conf. Content:\n{pip_config_content}" + assert ( + "trusted-host = mirrors.cloud.aliyuncs.com" in pip_config_content + ), f"trusted-host not found in pip.conf. Content:\n{pip_config_content}" + logger.info(f"PIP config verified successfully:\n{pip_config_content}") + + logger.info("Testing PIP mirror (https)...") + result = await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com", + ) + assert result.exit_code == 0, f"PIP aliyun mirror failed: {result.output}" + logger.info("PIP aliyun mirror configured successfully") + + check_result = await sandbox.execute(Command(command=["cat", "/root/.pip/pip.conf"])) + assert check_result.exit_code == 0, "Failed to read /root/.pip/pip.conf after updating" + pip_config_content = check_result.stdout + assert ( + "mirrors.aliyun.com/pypi/simple/" in pip_config_content + ), f"Updated PIP mirror URL not found. Content:\n{pip_config_content}" + + +async def _assert_speedup_github(sandbox: Sandbox): + logger.info("Testing GitHub acceleration...") + result = await sandbox.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="11.11.11.11", + ) + assert result.exit_code == 0, f"GitHub acceleration failed: {result.output}" + logger.info("GitHub acceleration configured successfully") + + logger.info("Verifying /etc/hosts content...") + check_result = await sandbox.execute(Command(command=["cat", "/etc/hosts"])) + assert check_result.exit_code == 0, "Failed to read /etc/hosts" + hosts_content = check_result.stdout + + assert "11.11.11.11 github.com" in hosts_content, f"Updated GitHub IP not found. Content:\n{hosts_content}" + logger.info(f"GitHub IP update verified successfully:\n{hosts_content}") + + +@pytest.mark.need_admin +@SKIP_IF_NO_DOCKER +@pytest.mark.asyncio +async def test_sandbox_speedup_all_in_one(sandbox_instance: Sandbox): + """Run all speedup checks in one sandbox.""" + await _assert_speedup_apt(sandbox_instance) + await _assert_speedup_pip(sandbox_instance) + await _assert_speedup_github(sandbox_instance) diff --git a/tests/unit/sdk/test_speedup.py b/tests/unit/sdk/test_speedup.py new file mode 100644 index 000000000..19db2ae07 --- /dev/null +++ b/tests/unit/sdk/test_speedup.py @@ -0,0 +1,81 @@ +"""Tests for sandbox speedup functionality.""" + +import pytest + +from rock.logger import init_logger + +logger = init_logger(__name__) + + +@pytest.mark.asyncio +async def test_apt_url_parsing(): + """Test APT mirror URL parsing and normalization.""" + from rock.sdk.sandbox.speedup.strategies.apt import AptSpeedupStrategy + + strategy = AptSpeedupStrategy() + + # HTTP URL without trailing slash + result = strategy.parse_value("http://mirrors.cloud.aliyuncs.com") + assert result["mirror_base"] == "http://mirrors.cloud.aliyuncs.com" + + # HTTPS URL with trailing slash should be normalized + result = strategy.parse_value("https://mirrors.aliyun.com/") + assert result["mirror_base"] == "https://mirrors.aliyun.com" + + # URL with custom path should be preserved + result = strategy.parse_value("http://custom.net/mirrors") + assert result["mirror_base"] == "http://custom.net/mirrors" + + logger.info("APT URL parsing tests passed") + + +@pytest.mark.asyncio +async def test_pip_url_parsing(): + """Test PIP mirror URL parsing and index path generation.""" + from rock.sdk.sandbox.speedup.strategies.pip import PipSpeedupStrategy + + strategy = PipSpeedupStrategy() + + # HTTP domain should generate correct index URL and extract trusted host + result = strategy.parse_value("http://mirrors.cloud.aliyuncs.com") + assert result["pip_index_url"] == "http://mirrors.cloud.aliyuncs.com/pypi/simple/" + assert result["pip_trusted_host"] == "mirrors.cloud.aliyuncs.com" + + # HTTPS domain with different host + result = strategy.parse_value("https://mirrors.aliyun.com") + assert result["pip_index_url"] == "https://mirrors.aliyun.com/pypi/simple/" + assert result["pip_trusted_host"] == "mirrors.aliyun.com" + + # URL with custom path should append index path correctly + result = strategy.parse_value("https://fake-url.com/1") + assert result["pip_index_url"] == "https://fake-url.com/1/pypi/simple/" + assert result["pip_trusted_host"] == "fake-url.com" + + # Trailing slash should be normalized before appending index path + result = strategy.parse_value("http://mirrors.cloud.aliyuncs.com/") + assert result["pip_index_url"] == "http://mirrors.cloud.aliyuncs.com/pypi/simple/" + assert result["pip_trusted_host"] == "mirrors.cloud.aliyuncs.com" + + logger.info("PIP URL parsing tests passed") + + +@pytest.mark.asyncio +async def test_github_ip_parsing(): + """Test GitHub IP address validation and hosts entry generation.""" + from rock.sdk.sandbox.speedup.strategies.github import GithubSpeedupStrategy + + strategy = GithubSpeedupStrategy() + + # Valid IP address should generate correct hosts entry + result = strategy.parse_value("11.11.11.11") + assert result["hosts_entry"] == "11.11.11.11 github.com" + + # Non-numeric format should raise ValueError + with pytest.raises(ValueError, match="Invalid IP address format"): + strategy.parse_value("invalid.ip.address") + + # IP octet out of valid range (0-255) should raise ValueError + with pytest.raises(ValueError, match="octet value must be 0-255"): + strategy.parse_value("256.1.1.1") + + logger.info("GitHub IP parsing tests passed") From 053c24800628024e26e609268c8c64ca09afaf1e Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 02/10] refactor: remove redundant timeout methods and reduce default timeout --- rock/sdk/sandbox/speedup/base.py | 2 +- rock/sdk/sandbox/speedup/strategies/apt.py | 4 ---- rock/sdk/sandbox/speedup/strategies/github.py | 4 ---- rock/sdk/sandbox/speedup/strategies/pip.py | 4 ---- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/rock/sdk/sandbox/speedup/base.py b/rock/sdk/sandbox/speedup/base.py index 8d09ec75d..08e5e2702 100644 --- a/rock/sdk/sandbox/speedup/base.py +++ b/rock/sdk/sandbox/speedup/base.py @@ -52,4 +52,4 @@ def get_nohup_wait_timeout(self) -> int: Returns: int: Timeout value """ - return 300 + return 30 diff --git a/rock/sdk/sandbox/speedup/strategies/apt.py b/rock/sdk/sandbox/speedup/strategies/apt.py index 6d09563fb..484244214 100644 --- a/rock/sdk/sandbox/speedup/strategies/apt.py +++ b/rock/sdk/sandbox/speedup/strategies/apt.py @@ -52,7 +52,3 @@ def generate_script(self, speedup_value: str) -> str: params = self.parse_value(speedup_value) logger.info(f"Generating APT speedup script with mirror: {params['mirror_base']}") return setup_apt_source_template.format(**params) - - def get_nohup_wait_timeout(self) -> int: - """APT update may take longer time""" - return 300 diff --git a/rock/sdk/sandbox/speedup/strategies/github.py b/rock/sdk/sandbox/speedup/strategies/github.py index fc01f4c44..67802aea6 100644 --- a/rock/sdk/sandbox/speedup/strategies/github.py +++ b/rock/sdk/sandbox/speedup/strategies/github.py @@ -66,7 +66,3 @@ def generate_script(self, speedup_value: str) -> str: params = self.parse_value(speedup_value) logger.info(f"Generating GitHub speedup script with hosts entry: {params['hosts_entry']}") return setup_github_hosts_template.format(**params) - - def get_nohup_wait_timeout(self) -> int: - """GitHub hosts configuration is very fast""" - return 30 diff --git a/rock/sdk/sandbox/speedup/strategies/pip.py b/rock/sdk/sandbox/speedup/strategies/pip.py index 827e5f6cc..c67d669c9 100644 --- a/rock/sdk/sandbox/speedup/strategies/pip.py +++ b/rock/sdk/sandbox/speedup/strategies/pip.py @@ -63,7 +63,3 @@ def generate_script(self, speedup_value: str) -> str: params = self.parse_value(speedup_value) logger.info(f"Generating PIP speedup script with mirror: {params['pip_index_url']}") return setup_pip_source_template.format(**params) - - def get_nohup_wait_timeout(self) -> int: - """PIP configuration is usually fast""" - return 60 From 6590b51ec618213b8fa60f9bf26002261afab22b Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 03/10] docs: update Python SDK and release notes documentation --- .../version-1.0.x/Release Notes/v1.0.0.md | 11 +++++++++++ .../version-1.0.x/Release Notes/v1.0.0.md | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/Release Notes/v1.0.0.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/Release Notes/v1.0.0.md index 36f516391..8d02970de 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/Release Notes/v1.0.0.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/Release Notes/v1.0.0.md @@ -31,6 +31,17 @@ - 简易的 Agent 安装接口 - 与Model-Service快速集成的能力 - 支持多种类型的Agent在隔离的沙箱环境中本地运行 + + +#### 沙箱加速 (Sandbox Speedup) +**[沙箱加速参考文档](../References/Python SDK References/python_sdk.md)** + +提供针对沙箱环境的网络优化与镜像配置能力: +- 支持一键配置 APT 公共镜像源 +- 支持 PIP 镜像源加速(自动处理 HTTP/HTTPS 协议及信任主机配置) +- 通过 Hosts 映射实现 GitHub 访问加速 +- 采用非侵入式配置,仅优化源地址而不强制执行安装 +- 基于策略(Strategy)的 URL 自动解析与标准化逻辑 --- ### 增强 diff --git a/docs/versioned_docs/version-1.0.x/Release Notes/v1.0.0.md b/docs/versioned_docs/version-1.0.x/Release Notes/v1.0.0.md index fd96342f1..2b5f38171 100644 --- a/docs/versioned_docs/version-1.0.x/Release Notes/v1.0.0.md +++ b/docs/versioned_docs/version-1.0.x/Release Notes/v1.0.0.md @@ -31,6 +31,16 @@ Features for quickly installing Agents in ROCK's Sandbox: - Quick integration capability with Model-Service - Support for running multiple Agents in isolated sandbox environments +#### Sandbox Speedup +**[Sandbox Speedup References Documentation](../References/Python SDK References/python_sdk.md)** + +Provides network optimization and mirror configuration capabilities for sandbox environments: +- Support for one-click APT public mirror configuration +- PIP mirror acceleration with automatic HTTP/HTTPS and trusted-host handling +- GitHub access acceleration via automated Hosts mapping +- Non-intrusive configuration focusing on source optimization without forced installation +- Strategy-based URL parsing and normalization logic + --- ### Enhancements From bb523b5732c2bef01c85b75a830b77f63dc4ce50 Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 04/10] docs: update Python SDK and release notes documentation --- .../Python SDK References/python_sdk.md | 105 ++++++++++++++++-- .../Python SDK References/python_sdk.md | 105 ++++++++++++++++-- 2 files changed, 194 insertions(+), 16 deletions(-) diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md index 96d41e489..41f0c130b 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md @@ -93,6 +93,102 @@ config = SandboxConfig( ) ``` +### 2.4 沙箱加速配置 + +ROCK 提供沙箱网络加速功能,支持配置 APT、PIP 和 GitHub 镜像源,提升受限网络环境下的包下载速度。 + +#### 支持的加速类型 + +**APT 镜像配置** + +配置 APT 包管理器镜像源,加速 Debian/Ubuntu 软件包下载。 + +```python +from rock.sdk.sandbox.speedup import SpeedupType + +# 配置 APT 镜像 +await sandbox.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com" +) +``` + +**PIP 镜像配置** + +配置 Python 包索引镜像,加速 pip 安装。 + +```python +# HTTP 镜像 +await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="http://mirrors.cloud.aliyuncs.com" +) + +# HTTPS 镜像 +await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com" +) +``` + +**GitHub 加速** + +通过添加自定义 DNS 解析条目加速 GitHub 访问。 + +```python +await sandbox.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="11.11.11.11" +) +``` + +#### 完整示例 + +```python +from rock.sdk.sandbox.speedup import SpeedupType +from rock.actions import RunMode + +async def setup_sandbox_with_speedup(): + """创建沙箱并配置加速""" + config = SandboxConfig(image="python:3.11") + sandbox = Sandbox(config) + + await sandbox.start() + + # 配置加速(在安装包之前配置) + await sandbox.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com" + ) + + await sandbox.arun(cmd="apt-get update && apt-get install -y git", mode=RunMode.NOHUP) + + await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com" + ) + + # speedup 不会主动安装 PIP,仅配置镜像源进行加速 + await sandbox.arun(cmd="pip install numpy", mode=RunMode.NOHUP) + + # 可以通过镜像 IP 加速 GitHub 访问 + await sandbox.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="11.11.11.11" + ) + + return sandbox +``` + +#### 注意事项 + +1. **配置顺序**: 在安装包之前配置加速 +2. **HTTPS vs HTTP**: HTTPS 镜像不需要为 PIP 配置 trusted-host +3. **GitHub IP**: 不同区域可能需要不同的 IP 以获得最佳性能 +4. **持久性**: 配置在沙箱生命周期内持久有效 +5. **多次调用**: 后续的加速调用会覆盖之前的配置 +6. **PIP 安装**: speedup 功能仅配置镜像源,不会自动安装 PIP + ## 3. GEM SDK ### 3.1 Python SDK 方式 @@ -160,11 +256,4 @@ if __name__ == "__main__": + "\n" ) main() -``` - -## 相关文档 - -- [快速开始指南](../../Getting%20Started/quickstart.md) - 了解如何快速开始使用 ROCK SDK -- [API 文档](../api.md) - 查看 SDK 封装的底层 API 接口 -- [配置指南](../../User%20Guides/configuration.md) - 了解 SDK 相关的配置选项 -- [安装指南](../../Getting%20Started/installation.md) - 详细了解 ROCK 安装和配置 \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md b/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md index c81cee4aa..aa761edb9 100644 --- a/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md +++ b/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md @@ -93,6 +93,102 @@ config = SandboxConfig( ) ``` +### 2.4 Sandbox Speedup Configuration + +ROCK provides sandbox network acceleration capabilities, supporting configuration of APT, PIP, and GitHub mirror sources to improve package download speeds in restricted network environments. + +#### Supported Speedup Types + +**APT Mirror Configuration** + +Configure APT package manager mirror sources for faster Debian/Ubuntu package downloads. + +```python +from rock.sdk.sandbox.speedup import SpeedupType + +# Configure APT mirror +await sandbox.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com" +) +``` + +**PIP Mirror Configuration** + +Configure Python package index mirrors for faster pip installations. + +```python +# HTTP mirror +await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="http://mirrors.cloud.aliyuncs.com" +) + +# HTTPS mirror +await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com" +) +``` + +**GitHub Acceleration** + +Configure GitHub IP acceleration by adding custom DNS resolution entries. + +```python +await sandbox.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="11.11.11.11" +) +``` + +#### Complete Example + +```python +from rock.sdk.sandbox.speedup import SpeedupType +from rock.actions import RunMode + +async def setup_sandbox_with_speedup(): + """Create sandbox and configure acceleration""" + config = SandboxConfig(image="python:3.11") + sandbox = Sandbox(config) + + await sandbox.start() + + # Configure acceleration (before installing packages) + await sandbox.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com" + ) + + await sandbox.arun(cmd="apt-get update && apt-get install -y git", mode=RunMode.NOHUP) + + await sandbox.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com" + ) + + # Speedup does not automatically install PIP, it only configures mirror sources for acceleration + await sandbox.arun(cmd="pip install numpy", mode=RunMode.NOHUP) + + # GitHub can be accelerated through mirror IP + await sandbox.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="11.11.11.11" + ) + + return sandbox +``` + +#### Important Notes + +1. **Configuration Order**: Configure speedup before installing packages +2. **HTTPS vs HTTP**: HTTPS mirrors don't require trusted-host configuration for PIP +3. **GitHub IP**: Different regions may require different IPs for optimal performance +4. **Persistence**: Configurations persist within the sandbox lifecycle +5. **Multiple Calls**: Subsequent speedup calls will override previous configurations +6. **PIP Installation**: The speedup feature only configures mirror sources and does not automatically install PIP + ## 3. GEM SDK ### 3.1 Python SDK Approach @@ -160,11 +256,4 @@ if __name__ == "__main__": + "\n" ) main() -``` - -## Related Documents - -- [Quick Start Guide](../../Getting%20Started/quickstart.md) - Learn how to quickly get started with the ROCK SDK -- [API Documentation](../api.md) - View the underlying API interfaces encapsulated by the SDK -- [Configuration Guide](../../User%20Guides/configuration.md) - Learn about SDK-related configuration options -- [Installation Guide](../../Getting%20Started/installation.md) - Detailed information about ROCK installation and setup \ No newline at end of file +``` \ No newline at end of file From 5160aeca43c5bfa9445ff7f8cd5116906499c5a4 Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 05/10] fix: add related documents links to Python SDK doc back --- .../References/Python SDK References/python_sdk.md | 8 +++++++- .../References/Python SDK References/python_sdk.md | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md index 41f0c130b..b7101b8c6 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md @@ -256,4 +256,10 @@ if __name__ == "__main__": + "\n" ) main() -``` \ No newline at end of file +``` + +## 相关文档 +- [快速开始指南](../../Getting%20Started/quickstart.md) - 了解如何快速开始使用 ROCK SDK +- [API 文档](../api.md) - 查看 SDK 封装的底层 API 接口 +- [配置指南](../../User%20Guides/configuration.md) - 了解 SDK 相关的配置选项 +- [安装指南](../../Getting%20Started/installation.md) - 详细了解 ROCK 安装和配置 \ No newline at end of file diff --git a/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md b/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md index aa761edb9..450c81164 100644 --- a/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md +++ b/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md @@ -256,4 +256,10 @@ if __name__ == "__main__": + "\n" ) main() -``` \ No newline at end of file +``` + +## Related Documents +- [Quick Start Guide](../../Getting%20Started/quickstart.md) - Learn how to quickly get started with the ROCK SDK +- [API Documentation](../api.md) - View the underlying API interfaces encapsulated by the SDK +- [Configuration Guide](../../User%20Guides/configuration.md) - Learn about SDK-related configuration options +- [Installation Guide](../../Getting%20Started/installation.md) - Detailed information about ROCK installation and setup \ No newline at end of file From 8e1634d6041facb633555bb1748807423bb4e083 Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 06/10] fix: update GitHub IP address in sandbox client as examples --- rock/sdk/sandbox/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rock/sdk/sandbox/client.py b/rock/sdk/sandbox/client.py index 88b8f23f9..7282485a2 100644 --- a/rock/sdk/sandbox/client.py +++ b/rock/sdk/sandbox/client.py @@ -831,7 +831,7 @@ async def speedup(self, speedup_type: SpeedupType, speedup_value: str, timeout: - PIP: Mirror URL with protocol Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" - GITHUB: IP address for github.com - Examples: "20.205.243.166" + Examples: "11.11.11.11" timeout: Execution timeout in seconds, default 300 Returns: @@ -853,7 +853,7 @@ async def speedup(self, speedup_type: SpeedupType, speedup_value: str, timeout: # Configure GitHub acceleration result = await sandbox.speedup( speedup_type=SpeedupType.GITHUB, - speedup_value="20.205.243.166" + speedup_value="11.11.11.11" ) """ executor = SpeedupExecutor(self) From d26ec553581208da758d6d51bd880f6063ff6745 Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:32:35 +0000 Subject: [PATCH 07/10] fix: update GitHub IP address in sandbox client as examples --- rock/sdk/sandbox/speedup/strategies/github.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rock/sdk/sandbox/speedup/strategies/github.py b/rock/sdk/sandbox/speedup/strategies/github.py index 67802aea6..70df272a7 100644 --- a/rock/sdk/sandbox/speedup/strategies/github.py +++ b/rock/sdk/sandbox/speedup/strategies/github.py @@ -36,8 +36,8 @@ def parse_value(self, speedup_value: str) -> dict[str, str]: speedup_value: IP address for github.com Examples: - "20.205.243.166" -> { - "hosts_entry": "20.205.243.166 github.com" + "11.11.11.11" -> { + "hosts_entry": "11.11.11.11 github.com" } """ # Trim whitespace From 830c2002a1b2e466ed545aa1f8e7f90078c1517d Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:41:05 +0000 Subject: [PATCH 08/10] refactor: move script execution logic to process.py --- rock/sdk/sandbox/client.py | 83 ----------------------------- rock/sdk/sandbox/process.py | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 83 deletions(-) create mode 100644 rock/sdk/sandbox/process.py diff --git a/rock/sdk/sandbox/client.py b/rock/sdk/sandbox/client.py index 7282485a2..7133de84e 100644 --- a/rock/sdk/sandbox/client.py +++ b/rock/sdk/sandbox/client.py @@ -736,89 +736,6 @@ async def _read_lines(start_line: int, end_line: int) -> str: result = ReadFileResponse(content=result) return result - async def execute_script( - self, - script_content: str, - script_name: str = None, - wait_timeout: int = 300, - wait_interval: int = 10, - cleanup: bool = True, - ) -> Observation: - """ - Execute a script in the sandbox. - - This is a general-purpose method that: - 1. Uploads the script to /tmp - 2. Executes it using nohup mode - 3. Optionally cleans up the script file - - Args: - script_content: The script content to execute - script_name: Optional custom script name. If None, generates timestamp-based name - wait_timeout: Maximum time to wait for script completion (seconds) - wait_interval: Interval between process checks (seconds) - cleanup: Whether to delete the script file after execution - - Returns: - Observation: Execution result - - Examples: - # Execute a simple script - result = await sandbox.execute_script( - script_content="#!/bin/bash\\necho 'Hello World'", - wait_timeout=60 - ) - - # Execute with custom name and keep the script - result = await sandbox.execute_script( - script_content=my_script, - script_name="my_custom_script.sh", - cleanup=False - ) - """ - # Generate script path - if script_name is None: - timestamp = str(time.time_ns()) - script_name = f"script_{timestamp}.sh" - - script_path = f"/tmp/{script_name}" - - try: - # Upload script - logger.info(f"Uploading script to {script_path}") - write_result = await self.write_file_by_path(script_content, script_path) - - if not write_result.success: - error_msg = f"Failed to upload script: {write_result.message}" - logger.error(error_msg) - return Observation(output=error_msg, exit_code=1, failure_reason="Script upload failed") - - # Execute script - logger.info(f"Executing script: {script_path} (timeout={wait_timeout}s)") - result = await self.arun( - cmd=f"bash {script_path}", - mode=RunMode.NOHUP, - wait_timeout=wait_timeout, - wait_interval=wait_interval, - ) - - return result - - except Exception as e: - error_msg = f"Script execution failed: {str(e)}" - logger.error(error_msg) - return Observation(output=error_msg, exit_code=1, failure_reason=error_msg) - - finally: - # Cleanup script if requested - if cleanup: - try: - logger.info(f"Cleaning up script: {script_path}") - await self.execute(Command(command=["rm", "-f", script_path])) - logger.debug(f"Script cleaned up successfully: {script_path}") - except Exception as e: - logger.warning(f"Failed to cleanup script {script_path}: {e}") - async def speedup(self, speedup_type: SpeedupType, speedup_value: str, timeout: int = 300) -> Observation: """ Configure acceleration for package managers or network resources diff --git a/rock/sdk/sandbox/process.py b/rock/sdk/sandbox/process.py new file mode 100644 index 000000000..b4ddce196 --- /dev/null +++ b/rock/sdk/sandbox/process.py @@ -0,0 +1,102 @@ +# rock/sdk/sandbox/process.py +import logging +import time + +from rock.actions import Command, Observation +from rock.actions.sandbox.base import AbstractSandbox + +logger = logging.getLogger(__name__) + + +class Process: + """Process management for sandbox execution""" + + def __init__(self, sandbox: AbstractSandbox): + self.sandbox = sandbox + + async def execute_script( + self, + script_content: str, + script_name: str | None = None, + wait_timeout: int = 300, + wait_interval: int = 10, + cleanup: bool = True, + ) -> Observation: + """ + Execute a script in the sandbox. + + This is a general-purpose method that: + 1. Uploads the script to /tmp + 2. Executes it using nohup mode + 3. Optionally cleans up the script file + + Args: + script_content: The script content to execute + script_name: Optional custom script name. If None, generates timestamp-based name + wait_timeout: Maximum time to wait for script completion (seconds) + wait_interval: Interval between process checks (seconds) + cleanup: Whether to delete the script file after execution + + Returns: + Observation: Execution result + + Examples: + # Execute a simple script + result = await sandbox.process.execute_script( + script_content="#!/bin/bash\\necho 'Hello World'", + wait_timeout=60 + ) + + # Execute with custom name and keep the script + result = await sandbox.process.execute_script( + script_content=my_script, + script_name="my_custom_script.sh", + cleanup=False + ) + """ + from rock.sdk.sandbox.client import Sandbox + + assert isinstance(self.sandbox, Sandbox) + + # Generate script path + if script_name is None: + timestamp = str(time.time_ns()) + script_name = f"script_{timestamp}.sh" + + script_path = f"/tmp/{script_name}" + + try: + # Upload script + logger.info(f"Uploading script to {script_path}") + write_result = await self.sandbox.write_file_by_path(script_content, script_path) + + if not write_result.success: + error_msg = f"Failed to upload script: {write_result.message}" + logger.error(error_msg) + return Observation(output=error_msg, exit_code=1, failure_reason="Script upload failed") + + # Execute script + logger.info(f"Executing script: {script_path} (timeout={wait_timeout}s)") + result = await self.sandbox.arun( + cmd=f"bash {script_path}", + mode="nohup", + wait_timeout=wait_timeout, + wait_interval=wait_interval, + ) + + return result + + except Exception as e: + error_msg = f"Script execution failed: {str(e)}" + logger.error(error_msg) + return Observation(output=error_msg, exit_code=1, failure_reason=error_msg) + + finally: + # Cleanup script if requested + if cleanup: + try: + logger.info(f"Cleaning up script: {script_path}") + await self.sandbox.execute(Command(command=["rm", "-f", script_path])) + logger.debug(f"Script cleaned up successfully: {script_path}") + except Exception as e: + logger.warning(f"Failed to cleanup script {script_path}: {e}") From ac31511c338250e503b06482807173c5caf86abf Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:50:11 +0000 Subject: [PATCH 09/10] refactor: move network speedup methods to `network` module --- .../Python SDK References/python_sdk.md | 14 ++--- .../Python SDK References/python_sdk.md | 14 ++--- rock/sdk/sandbox/client.py | 47 ++------------ rock/sdk/sandbox/network.py | 62 +++++++++++++++++++ rock/sdk/sandbox/speedup/executor.py | 2 +- tests/integration/sdk/sandbox/test_speedup.py | 8 +-- 6 files changed, 87 insertions(+), 60 deletions(-) create mode 100644 rock/sdk/sandbox/network.py diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md index b7101b8c6..c1083f29b 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.x/References/Python SDK References/python_sdk.md @@ -107,7 +107,7 @@ ROCK 提供沙箱网络加速功能,支持配置 APT、PIP 和 GitHub 镜像 from rock.sdk.sandbox.speedup import SpeedupType # 配置 APT 镜像 -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.APT, speedup_value="http://mirrors.cloud.aliyuncs.com" ) @@ -119,13 +119,13 @@ await sandbox.speedup( ```python # HTTP 镜像 -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="http://mirrors.cloud.aliyuncs.com" ) # HTTPS 镜像 -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="https://mirrors.aliyun.com" ) @@ -136,7 +136,7 @@ await sandbox.speedup( 通过添加自定义 DNS 解析条目加速 GitHub 访问。 ```python -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.GITHUB, speedup_value="11.11.11.11" ) @@ -156,14 +156,14 @@ async def setup_sandbox_with_speedup(): await sandbox.start() # 配置加速(在安装包之前配置) - await sandbox.speedup( + await sandbox.network.speedup( speedup_type=SpeedupType.APT, speedup_value="http://mirrors.cloud.aliyuncs.com" ) await sandbox.arun(cmd="apt-get update && apt-get install -y git", mode=RunMode.NOHUP) - await sandbox.speedup( + await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="https://mirrors.aliyun.com" ) @@ -172,7 +172,7 @@ async def setup_sandbox_with_speedup(): await sandbox.arun(cmd="pip install numpy", mode=RunMode.NOHUP) # 可以通过镜像 IP 加速 GitHub 访问 - await sandbox.speedup( + await sandbox.network.speedup( speedup_type=SpeedupType.GITHUB, speedup_value="11.11.11.11" ) diff --git a/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md b/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md index 450c81164..5272d7edb 100644 --- a/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md +++ b/docs/versioned_docs/version-1.0.x/References/Python SDK References/python_sdk.md @@ -107,7 +107,7 @@ Configure APT package manager mirror sources for faster Debian/Ubuntu package do from rock.sdk.sandbox.speedup import SpeedupType # Configure APT mirror -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.APT, speedup_value="http://mirrors.cloud.aliyuncs.com" ) @@ -119,13 +119,13 @@ Configure Python package index mirrors for faster pip installations. ```python # HTTP mirror -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="http://mirrors.cloud.aliyuncs.com" ) # HTTPS mirror -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="https://mirrors.aliyun.com" ) @@ -136,7 +136,7 @@ await sandbox.speedup( Configure GitHub IP acceleration by adding custom DNS resolution entries. ```python -await sandbox.speedup( +await sandbox.network.speedup( speedup_type=SpeedupType.GITHUB, speedup_value="11.11.11.11" ) @@ -156,14 +156,14 @@ async def setup_sandbox_with_speedup(): await sandbox.start() # Configure acceleration (before installing packages) - await sandbox.speedup( + await sandbox.network.speedup( speedup_type=SpeedupType.APT, speedup_value="http://mirrors.cloud.aliyuncs.com" ) await sandbox.arun(cmd="apt-get update && apt-get install -y git", mode=RunMode.NOHUP) - await sandbox.speedup( + await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="https://mirrors.aliyun.com" ) @@ -172,7 +172,7 @@ async def setup_sandbox_with_speedup(): await sandbox.arun(cmd="pip install numpy", mode=RunMode.NOHUP) # GitHub can be accelerated through mirror IP - await sandbox.speedup( + await sandbox.network.speedup( speedup_type=SpeedupType.GITHUB, speedup_value="11.11.11.11" ) diff --git a/rock/sdk/sandbox/client.py b/rock/sdk/sandbox/client.py index 7133de84e..f316ed03b 100644 --- a/rock/sdk/sandbox/client.py +++ b/rock/sdk/sandbox/client.py @@ -42,8 +42,9 @@ from rock.sdk.sandbox.agent.base import Agent from rock.sdk.sandbox.config import SandboxConfig, SandboxGroupConfig from rock.sdk.sandbox.model_service.base import ModelService +from rock.sdk.sandbox.network import Network +from rock.sdk.sandbox.process import Process from rock.sdk.sandbox.remote_user import LinuxRemoteUser, RemoteUser -from rock.sdk.sandbox.speedup import SpeedupExecutor, SpeedupType from rock.utils import HttpUtils, extract_nohup_pid, retry_async logger = logging.getLogger(__name__) @@ -66,6 +67,8 @@ class Sandbox(AbstractSandbox): agent: Agent | None = None model_service: ModelService | None = None remote_user: RemoteUser | None = None + process: Process | None = None + network: Network | None = None def __init__(self, config: SandboxConfig): self._pod_name = None @@ -81,6 +84,8 @@ def __init__(self, config: SandboxConfig): self._oss_token_expire_time = self._generate_utc_iso_time() self._cluster = self.config.cluster self.remote_user = LinuxRemoteUser(self) + self.process = Process(self) + self.network = Network(self) @property def sandbox_id(self) -> str: @@ -736,46 +741,6 @@ async def _read_lines(start_line: int, end_line: int) -> str: result = ReadFileResponse(content=result) return result - async def speedup(self, speedup_type: SpeedupType, speedup_value: str, timeout: int = 300) -> Observation: - """ - Configure acceleration for package managers or network resources - - Args: - speedup_type: Type of speedup configuration (SpeedupType.APT, SpeedupType.PIP, SpeedupType.GITHUB) - speedup_value: Speedup value, format depends on speedup_type: - - APT: Mirror URL with protocol - Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" - - PIP: Mirror URL with protocol - Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" - - GITHUB: IP address for github.com - Examples: "11.11.11.11" - timeout: Execution timeout in seconds, default 300 - - Returns: - Observation: Execution result containing output and exit code - - Examples: - # Configure APT mirror - result = await sandbox.speedup( - speedup_type=SpeedupType.APT, - speedup_value="http://mirrors.cloud.aliyuncs.com" - ) - - # Configure PIP mirror with custom path - result = await sandbox.speedup( - speedup_type=SpeedupType.PIP, - speedup_value="https://mirrors.aliyun.com" - ) - - # Configure GitHub acceleration - result = await sandbox.speedup( - speedup_type=SpeedupType.GITHUB, - speedup_value="11.11.11.11" - ) - """ - executor = SpeedupExecutor(self) - return await executor.execute(speedup_type, speedup_value, timeout) - async def _generate_tmp_session_name(self) -> str: timestamp = str(time.time_ns()) return f"bash-{timestamp}" diff --git a/rock/sdk/sandbox/network.py b/rock/sdk/sandbox/network.py new file mode 100644 index 000000000..137e1578f --- /dev/null +++ b/rock/sdk/sandbox/network.py @@ -0,0 +1,62 @@ +"""Network management for sandbox.""" + +from __future__ import annotations # Postpone annotation evaluation to avoid circular imports. + +import logging +from typing import TYPE_CHECKING + +from rock.actions import Observation +from rock.sdk.sandbox.speedup.executor import SpeedupExecutor +from rock.sdk.sandbox.speedup.types import SpeedupType + +if TYPE_CHECKING: + from rock.sdk.sandbox.client import Sandbox + +logger = logging.getLogger(__name__) + + +class Network: + """Network management for sandbox""" + + def __init__(self, sandbox: Sandbox): + self.sandbox = sandbox + self._speedup_executor = SpeedupExecutor(sandbox) + + async def speedup(self, speedup_type: SpeedupType, speedup_value: str, timeout: int = 300) -> Observation: + """ + Configure acceleration for package managers or network resources + + Args: + speedup_type: Type of speedup configuration (SpeedupType.APT, SpeedupType.PIP, SpeedupType.GITHUB) + speedup_value: Speedup value, format depends on speedup_type: + - APT: Mirror URL with protocol + Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" + - PIP: Mirror URL with protocol + Examples: "http://mirrors.cloud.aliyuncs.com", "https://mirrors.aliyun.com" + - GITHUB: IP address for github.com + Examples: "11.11.11.11" + timeout: Execution timeout in seconds, default 300 + + Returns: + Observation: Execution result containing output and exit code + + Examples: + # Configure APT mirror + result = await sandbox.network.speedup( + speedup_type=SpeedupType.APT, + speedup_value="http://mirrors.cloud.aliyuncs.com" + ) + + # Configure PIP mirror with custom path + result = await sandbox.network.speedup( + speedup_type=SpeedupType.PIP, + speedup_value="https://mirrors.aliyun.com" + ) + + # Configure GitHub acceleration + result = await sandbox.network.speedup( + speedup_type=SpeedupType.GITHUB, + speedup_value="11.11.11.11" + ) + """ + return await self._speedup_executor.execute(speedup_type, speedup_value, timeout) diff --git a/rock/sdk/sandbox/speedup/executor.py b/rock/sdk/sandbox/speedup/executor.py index 7b7b51671..d3ce7c441 100644 --- a/rock/sdk/sandbox/speedup/executor.py +++ b/rock/sdk/sandbox/speedup/executor.py @@ -86,7 +86,7 @@ async def execute(self, speedup_type: SpeedupType, speedup_value: str, timeout: return Observation(output=error_msg, exit_code=1, failure_reason="Script generation failed") # 4. Execute script using the general execute_script method - result = await self.sandbox.execute_script( + result = await self.sandbox.process.execute_script( script_content=script_content, wait_timeout=strategy.get_nohup_wait_timeout(), cleanup=True, diff --git a/tests/integration/sdk/sandbox/test_speedup.py b/tests/integration/sdk/sandbox/test_speedup.py index 1453a9d97..c9bd5f9cb 100644 --- a/tests/integration/sdk/sandbox/test_speedup.py +++ b/tests/integration/sdk/sandbox/test_speedup.py @@ -13,7 +13,7 @@ async def _assert_speedup_apt(sandbox: Sandbox): logger.info("Testing APT public mirror configuration...") - result = await sandbox.speedup( + result = await sandbox.network.speedup( speedup_type=SpeedupType.APT, speedup_value="http://mirrors.cloud.aliyuncs.com", ) @@ -36,7 +36,7 @@ async def _assert_speedup_apt(sandbox: Sandbox): async def _assert_speedup_pip(sandbox: Sandbox): logger.info("Testing PIP mirror (http)...") - result = await sandbox.speedup( + result = await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="http://mirrors.cloud.aliyuncs.com", ) @@ -57,7 +57,7 @@ async def _assert_speedup_pip(sandbox: Sandbox): logger.info(f"PIP config verified successfully:\n{pip_config_content}") logger.info("Testing PIP mirror (https)...") - result = await sandbox.speedup( + result = await sandbox.network.speedup( speedup_type=SpeedupType.PIP, speedup_value="https://mirrors.aliyun.com", ) @@ -74,7 +74,7 @@ async def _assert_speedup_pip(sandbox: Sandbox): async def _assert_speedup_github(sandbox: Sandbox): logger.info("Testing GitHub acceleration...") - result = await sandbox.speedup( + result = await sandbox.network.speedup( speedup_type=SpeedupType.GITHUB, speedup_value="11.11.11.11", ) From 681af4a910d36e646d68fb3d64d7148232935d1a Mon Sep 17 00:00:00 2001 From: "pengshixin.psx" Date: Tue, 13 Jan 2026 06:54:54 +0000 Subject: [PATCH 10/10] refactor: replace logger initialization in network and process modules --- rock/sdk/sandbox/network.py | 6 ++---- rock/sdk/sandbox/process.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/rock/sdk/sandbox/network.py b/rock/sdk/sandbox/network.py index 137e1578f..0fe77caa0 100644 --- a/rock/sdk/sandbox/network.py +++ b/rock/sdk/sandbox/network.py @@ -1,18 +1,16 @@ -"""Network management for sandbox.""" - from __future__ import annotations # Postpone annotation evaluation to avoid circular imports. -import logging from typing import TYPE_CHECKING from rock.actions import Observation +from rock.logger import init_logger from rock.sdk.sandbox.speedup.executor import SpeedupExecutor from rock.sdk.sandbox.speedup.types import SpeedupType if TYPE_CHECKING: from rock.sdk.sandbox.client import Sandbox -logger = logging.getLogger(__name__) +logger = init_logger(__name__) class Network: diff --git a/rock/sdk/sandbox/process.py b/rock/sdk/sandbox/process.py index b4ddce196..11fc5b301 100644 --- a/rock/sdk/sandbox/process.py +++ b/rock/sdk/sandbox/process.py @@ -1,17 +1,21 @@ -# rock/sdk/sandbox/process.py -import logging +from __future__ import annotations # Postpone annotation evaluation to avoid circular imports. + import time +from typing import TYPE_CHECKING from rock.actions import Command, Observation -from rock.actions.sandbox.base import AbstractSandbox +from rock.logger import init_logger + +if TYPE_CHECKING: + from rock.sdk.sandbox.client import Sandbox -logger = logging.getLogger(__name__) +logger = init_logger(__name__) class Process: """Process management for sandbox execution""" - def __init__(self, sandbox: AbstractSandbox): + def __init__(self, sandbox: Sandbox): self.sandbox = sandbox async def execute_script(