mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-05-27 03:05:59 +08:00
### What problem does this PR solve? Feat: add local & ssh provider in admin panel ### Type of change - [x] New Feature (non-breaking change which adds functionality)
260 lines
7.6 KiB
Python
260 lines
7.6 KiB
Python
#
|
|
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
|
|
#
|
|
# 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
|
|
#
|
|
# http://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.
|
|
#
|
|
|
|
"""
|
|
Sandbox client for agent components.
|
|
|
|
This module provides a unified interface for agent components to interact
|
|
with the configured sandbox provider.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from typing import Dict, Any, Optional
|
|
|
|
from api.db.services.system_settings_service import SystemSettingsService
|
|
from agent.sandbox.providers import ProviderManager
|
|
from agent.sandbox.providers.base import ExecutionResult, SandboxProviderConfigError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# Global provider manager instance
|
|
_provider_manager: Optional[ProviderManager] = None
|
|
|
|
|
|
def get_provider_manager() -> ProviderManager:
|
|
"""
|
|
Get the global provider manager instance.
|
|
|
|
Returns:
|
|
ProviderManager instance with active provider loaded
|
|
"""
|
|
global _provider_manager
|
|
|
|
if _provider_manager is not None:
|
|
return _provider_manager
|
|
|
|
_provider_manager = ProviderManager()
|
|
_load_provider_from_settings()
|
|
|
|
return _provider_manager
|
|
|
|
|
|
def _load_provider_from_settings() -> None:
|
|
"""
|
|
Load sandbox provider from system settings and configure the provider manager.
|
|
|
|
This function resolves the active provider type, then loads configuration
|
|
from system settings.
|
|
"""
|
|
global _provider_manager
|
|
|
|
if _provider_manager is None:
|
|
return
|
|
|
|
try:
|
|
provider_type = _resolve_provider_type()
|
|
config = _load_provider_config(provider_type)
|
|
|
|
# Import and instantiate the provider
|
|
from agent.sandbox.providers import (
|
|
SelfManagedProvider,
|
|
AliyunCodeInterpreterProvider,
|
|
E2BProvider,
|
|
LocalProvider,
|
|
SSHProvider,
|
|
)
|
|
|
|
provider_classes = {
|
|
"self_managed": SelfManagedProvider,
|
|
"aliyun_codeinterpreter": AliyunCodeInterpreterProvider,
|
|
"e2b": E2BProvider,
|
|
"local": LocalProvider,
|
|
"ssh": SSHProvider,
|
|
}
|
|
|
|
if provider_type not in provider_classes:
|
|
logger.error(f"Unknown provider type: {provider_type}")
|
|
return
|
|
|
|
provider_class = provider_classes[provider_type]
|
|
provider = provider_class()
|
|
|
|
# Initialize the provider
|
|
if not provider.initialize(config):
|
|
message = f"Failed to initialize sandbox provider: {provider_type}. Config keys: {list(config.keys())}"
|
|
if provider_type in {"local", "ssh"}:
|
|
raise SandboxProviderConfigError(message)
|
|
logger.error(message)
|
|
return
|
|
|
|
# Set the active provider
|
|
_provider_manager.set_provider(provider_type, provider)
|
|
logger.info(f"Sandbox provider '{provider_type}' initialized successfully")
|
|
|
|
except SandboxProviderConfigError:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to load sandbox provider from settings: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
def _load_provider_config_from_settings(provider_type: str) -> Dict[str, Any]:
|
|
provider_config_settings = SystemSettingsService.get_by_name(f"sandbox.{provider_type}")
|
|
if not provider_config_settings:
|
|
logger.warning(f"No configuration found for provider: {provider_type}")
|
|
return {}
|
|
|
|
try:
|
|
return json.loads(provider_config_settings[0].value)
|
|
except json.JSONDecodeError as e:
|
|
logger.error(f"Failed to parse sandbox config for {provider_type}: {e}")
|
|
return {}
|
|
|
|
|
|
def _resolve_provider_type() -> str:
|
|
provider_type_settings = SystemSettingsService.get_by_name("sandbox.provider_type")
|
|
if not provider_type_settings:
|
|
return "self_managed"
|
|
return provider_type_settings[0].value
|
|
|
|
|
|
def _load_provider_config(provider_type: str) -> Dict[str, Any]:
|
|
return _load_provider_config_from_settings(provider_type)
|
|
|
|
|
|
def reload_provider() -> None:
|
|
"""
|
|
Reload the sandbox provider from system settings.
|
|
|
|
Use this function when sandbox settings have been updated.
|
|
"""
|
|
global _provider_manager
|
|
_provider_manager = None
|
|
_load_provider_from_settings()
|
|
|
|
|
|
def execute_code(
|
|
code: str,
|
|
language: str = "python",
|
|
timeout: int = 30,
|
|
arguments: Optional[Dict[str, Any]] = None
|
|
) -> ExecutionResult:
|
|
"""
|
|
Execute code in the configured sandbox.
|
|
|
|
This is the main entry point for agent components to execute code.
|
|
|
|
Args:
|
|
code: Source code to execute
|
|
language: Programming language (python, nodejs, javascript)
|
|
timeout: Maximum execution time in seconds
|
|
arguments: Optional arguments dict to pass to main() function
|
|
|
|
Returns:
|
|
ExecutionResult containing stdout, stderr, exit_code, and metadata
|
|
|
|
Raises:
|
|
RuntimeError: If no provider is configured or execution fails
|
|
"""
|
|
provider_manager = get_provider_manager()
|
|
|
|
if not provider_manager.is_configured():
|
|
raise RuntimeError(
|
|
"No sandbox provider configured. Please configure sandbox settings in the admin panel."
|
|
)
|
|
|
|
provider = provider_manager.get_provider()
|
|
provider_name = provider_manager.get_provider_name() or getattr(provider, "__class__", type(provider)).__name__
|
|
|
|
logger.info(
|
|
"CodeExec using sandbox provider '%s' (language=%s, timeout=%ss)",
|
|
provider_name,
|
|
language,
|
|
timeout,
|
|
)
|
|
|
|
# Create a sandbox instance
|
|
instance = provider.create_instance(template=language)
|
|
|
|
try:
|
|
# Execute the code
|
|
result = provider.execute_code(
|
|
instance_id=instance.instance_id,
|
|
code=code,
|
|
language=language,
|
|
timeout=timeout,
|
|
arguments=arguments
|
|
)
|
|
|
|
return result
|
|
|
|
finally:
|
|
# Clean up the instance
|
|
try:
|
|
provider.destroy_instance(instance.instance_id)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to destroy sandbox instance {instance.instance_id}: {e}")
|
|
|
|
|
|
def health_check() -> bool:
|
|
"""
|
|
Check if the sandbox provider is healthy.
|
|
|
|
Returns:
|
|
True if provider is configured and healthy, False otherwise
|
|
"""
|
|
try:
|
|
provider_manager = get_provider_manager()
|
|
|
|
if not provider_manager.is_configured():
|
|
return False
|
|
|
|
provider = provider_manager.get_provider()
|
|
return provider.health_check()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Sandbox health check failed: {e}")
|
|
return False
|
|
|
|
|
|
def get_provider_info() -> Dict[str, Any]:
|
|
"""
|
|
Get information about the current sandbox provider.
|
|
|
|
Returns:
|
|
Dictionary with provider information:
|
|
- provider_type: Type of the active provider
|
|
- configured: Whether provider is configured
|
|
- healthy: Whether provider is healthy
|
|
"""
|
|
try:
|
|
provider_manager = get_provider_manager()
|
|
|
|
return {
|
|
"provider_type": provider_manager.get_provider_name(),
|
|
"configured": provider_manager.is_configured(),
|
|
"healthy": health_check(),
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get provider info: {e}")
|
|
return {
|
|
"provider_type": None,
|
|
"configured": False,
|
|
"healthy": False,
|
|
}
|