Skip to content

Dynamic Package Import

Nekro Agent provides dynamic package import functionality, allowing plugins to install and import Python third-party packages on-demand at runtime. This feature enables plugins to flexibly utilize various tools and frameworks from the Python ecosystem without modifying the system environment.

Feature Overview

The dynamic package import functionality has the following characteristics:

  • On-demand Installation: Downloads and installs packages only when needed, saving system resources
  • Version Control: Supports precise version specifications and dependency constraints
  • Isolated Storage: Packages are installed in plugin-specific directories without affecting the system Python environment
  • Mirror Support: Supports configuring PyPI mirror sources to accelerate domestic downloads
  • Error Handling: Provides friendly error prompts and exception handling mechanisms

Basic Usage

Import Function

Import the dynamic package import function from nekro_agent.api.plugin:

python
from nekro_agent.api.plugin import dynamic_import_pkg
from nekro_agent.api.schemas import AgentCtx

Simple Import Example

The simplest usage is to directly provide the package name:

python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "use_requests", "Use requests library to send HTTP requests")
async def fetch_url(_ctx: AgentCtx, url: str) -> str:
    """Get web page content using dynamically imported requests library"""

    # Dynamically import requests package
    requests = dynamic_import_pkg("requests")

    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        return f"Successfully fetched page, status code: {response.status_code}, content length: {len(response.text)} characters"
    except Exception as e:
        return f"Request failed: {e}"

Version Specifications

Specifying Version Ranges

Supports standard Python package version specification syntax:

python
# Exact version
pandas = dynamic_import_pkg("pandas==2.0.0")

# Minimum version requirement
numpy = dynamic_import_pkg("numpy>=1.24.0")

# Version range
requests = dynamic_import_pkg("requests>=2.25.0,<3.0.0")

# Exclude specific version
flask = dynamic_import_pkg("flask>=2.0,!=2.0.1")

Version Specification Example

python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "data_analysis", "Execute data analysis")
async def analyze_data(_ctx: AgentCtx, data: list) -> str:
    """Perform data analysis using pandas"""

    # Import specific version of pandas
    pd = dynamic_import_pkg(
        "pandas>=2.0.0,<3.0.0",
        import_name="pandas"  # Specify module name for import
    )

    try:
        df = pd.DataFrame(data)
        summary = df.describe().to_string()
        return f"Data analysis results:\n{summary}"
    except Exception as e:
        return f"Analysis failed: {e}"

Advanced Parameter Configuration

Complete Parameter Description

python
dynamic_import_pkg(
    package_spec: str,                    # Package specification, e.g., "requests>=2.25.0"
    import_name: Optional[str] = None,    # Import name, defaults to package name
    mirror: Optional[str] = "https://pypi.tuna.tsinghua.edu.cn/simple",  # PyPI mirror source
    trusted_host: bool = True,            # Whether to trust mirror source host
    timeout: int = 300,                   # Installation timeout (seconds)
    repo_dir: Optional[Path] = None       # Custom installation directory
)

Using Domestic Mirrors for Acceleration

Uses Tsinghua University PyPI mirror source by default, but you can also specify other mirrors:

python
# Use Alibaba Cloud mirror
beautifulsoup4 = dynamic_import_pkg(
    "beautifulsoup4",
    mirror="https://mirrors.aliyun.com/pypi/simple/",
    trusted_host=True
)

# Use Tencent Cloud mirror
pillow = dynamic_import_pkg(
    "Pillow>=10.0.0",
    mirror="https://mirrors.cloud.tencent.com/pypi/simple",
    trusted_host=True
)

# Use official PyPI (slower)
scipy = dynamic_import_pkg(
    "scipy",
    mirror="https://pypi.org/simple",
    trusted_host=False
)

Practical Usage Examples

Example 1: Web Scraping

python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "scrape_webpage", "Scrape web page content")
async def scrape_webpage(_ctx: AgentCtx, url: str, selector: str) -> str:
    """Use BeautifulSoup to scrape specified elements from web pages"""

    # Dynamically import required packages
    requests = dynamic_import_pkg("requests>=2.25.0")
    bs4 = dynamic_import_pkg("beautifulsoup4>=4.9.0", import_name="bs4")

    try:
        # Get web page content
        response = requests.get(url, timeout=10)
        response.raise_for_status()

        # Parse HTML
        soup = bs4.BeautifulSoup(response.text, 'html.parser')
        elements = soup.select(selector)

        if not elements:
            return f"No elements found matching selector '{selector}'"

        # Extract text content
        results = [elem.get_text(strip=True) for elem in elements[:5]]  # Return up to 5
        return f"Found {len(elements)} elements, first {len(results)} contents:\n" + "\n".join(results)

    except Exception as e:
        return f"Scraping failed: {e}"

Example 2: Image Processing

python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "process_image", "Process image files")
async def process_image(_ctx: AgentCtx, image_path: str, operation: str) -> str:
    """Use Pillow to process images"""

    # Dynamically import Pillow
    PIL = dynamic_import_pkg("Pillow>=10.0.0", import_name="PIL")
    from PIL import Image, ImageFilter

    try:
        # Get actual file path
        real_path = _ctx.fs.get_file(image_path)

        # Open image
        img = Image.open(real_path)

        # Process based on operation type
        if operation == "blur":
            processed = img.filter(ImageFilter.BLUR)
        elif operation == "grayscale":
            processed = img.convert('L')
        elif operation == "resize":
            processed = img.resize((800, 600))
        else:
            return f"Unsupported operation: {operation}"

        # Save processed image
        output_path = _ctx.fs.shared_path / f"processed_{operation}.jpg"
        processed.save(output_path)

        # Return sandbox path to AI
        sandbox_path = _ctx.fs.forward_file(output_path)
        return f"Image processing completed, result: {sandbox_path}"

    except Exception as e:
        return f"Image processing failed: {e}"

Example 3: Data Processing and Visualization

python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "create_chart", "Create data chart")
async def create_data_chart(_ctx: AgentCtx, data: dict, chart_type: str) -> str:
    """Create data charts using matplotlib"""

    # Dynamically import data processing and visualization libraries
    pd = dynamic_import_pkg("pandas>=2.0.0", import_name="pandas")
    plt = dynamic_import_pkg("matplotlib>=3.5.0", import_name="matplotlib.pyplot")

    try:
        # Create DataFrame
        df = pd.DataFrame(data)

        # Create chart
        fig, ax = plt.subplots(figsize=(10, 6))

        if chart_type == "bar":
            df.plot(kind='bar', ax=ax)
        elif chart_type == "line":
            df.plot(kind='line', ax=ax)
        elif chart_type == "pie":
            df.plot(kind='pie', y=df.columns[0], ax=ax)
        else:
            return f"Unsupported chart type: {chart_type}"

        # Save chart
        output_path = _ctx.fs.shared_path / f"chart_{chart_type}.png"
        plt.savefig(output_path, dpi=150, bbox_inches='tight')
        plt.close(fig)

        # Return sandbox path
        sandbox_path = _ctx.fs.forward_file(output_path)
        return f"Chart generated: {sandbox_path}"

    except Exception as e:
        return f"Chart generation failed: {e}"

Example 4: Scientific Computing

python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "scientific_compute", "Execute scientific computing")
async def scientific_compute(_ctx: AgentCtx, operation: str, values: list) -> str:
    """Perform scientific computing using NumPy and SciPy"""

    # Dynamically import scientific computing libraries
    np = dynamic_import_pkg("numpy>=1.24.0", import_name="numpy")
    scipy = dynamic_import_pkg("scipy>=1.10.0")

    try:
        data = np.array(values)

        if operation == "fft":
            # Fast Fourier Transform
            result = np.fft.fft(data)
            return f"FFT results (first 5): {result[:5]}"

        elif operation == "stats":
            # Statistical analysis
            from scipy import stats
            mean = np.mean(data)
            std = np.std(data)
            skew = stats.skew(data)
            kurtosis = stats.kurtosis(data)

            return f"""Statistical results:
- Mean: {mean:.4f}
- Standard deviation: {std:.4f}
- Skewness: {skew:.4f}
- Kurtosis: {kurtosis:.4f}"""

        elif operation == "interpolate":
            # Data interpolation
            from scipy import interpolate
            x = np.arange(len(data))
            f = interpolate.interp1d(x, data, kind='cubic')
            x_new = np.linspace(0, len(data)-1, len(data)*2)
            y_new = f(x_new)
            return f"Interpolation completed, original data points: {len(data)}, after interpolation: {len(y_new)}"

        else:
            return f"Unsupported operation: {operation}"

    except Exception as e:
        return f"Computation failed: {e}"

Error Handling

Dynamic imports may fail due to network issues, non-existent packages, or other reasons

Common Error Types

python
# RuntimeError: Installation failed
# - Network connection problems
# - Package does not exist or version unavailable
# - SSL certificate verification failed
# - Mirror source access denied

# ImportError: Import failed
# - Package installed but module cannot be found
# - Import name does not match actual module name

# ValueError: Package specification format error
# - Incorrect version number format
# - Unsupported version operators used

# subprocess.TimeoutExpired: Installation timeout
# - Package size too large
# - Network speed too slow

Considerations

Security Considerations

  1. Only import trusted packages: Only install packages from official PyPI or trusted mirror sources
  2. Version locking: Use exact version numbers in production environments to avoid unexpected package updates
  3. Review dependencies: Understand transitive dependencies of packages to avoid introducing unnecessary risks
  4. Document declaration: Declare used packages in plugin documentation and note version numbers

Performance Considerations

  1. First installation overhead: The first time a package is used, there will be download and installation time
  2. Disk space: Each package occupies disk space; be mindful of controlling the number and size of packages

Compatibility Considerations

  1. Python version: Ensure packages support the Python version running Nekro Agent
  2. System dependencies: Some packages may require system-level dependency libraries (e.g., OpenCV requires specific C++ libraries)
  3. Package conflicts: Be aware of potential dependency conflicts between different packages

Troubleshooting

Installation Succeeded but Import Failed

Error: ImportError: Installation succeeded but cannot import module_name

Solution:
1. Check if the import_name parameter is correct
2. Some packages have different import names than package names (e.g., Pillow -> PIL)
3. Package may require additional system dependencies

By properly utilizing the dynamic package import functionality, your plugin can flexibly leverage Python's rich ecosystem to implement more powerful and diverse features.