import importlib from openhands.runtime.base import Runtime from openhands.runtime.impl.cli.cli_runtime import CLIRuntime from openhands.runtime.impl.docker.docker_runtime import ( DockerRuntime, ) from openhands.runtime.impl.kubernetes.kubernetes_runtime import KubernetesRuntime from openhands.runtime.impl.local.local_runtime import LocalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.utils.import_utils import get_impl # mypy: disable-error-code="type-abstract" _DEFAULT_RUNTIME_CLASSES: dict[str, type[Runtime]] = { 'eventstream': DockerRuntime, 'docker': DockerRuntime, 'remote': RemoteRuntime, 'local': LocalRuntime, 'kubernetes': KubernetesRuntime, 'cli': CLIRuntime, } # Try to import third-party runtimes if available _THIRD_PARTY_RUNTIME_CLASSES: dict[str, type[Runtime]] = {} # Dynamically discover and import third-party runtimes # Check if third_party package exists and discover runtimes try: import third_party.runtime.impl third_party_base = 'third_party.runtime.impl' # List of potential third-party runtime modules to try # These are discovered from the third_party directory structure potential_runtimes = [] try: import pkgutil for importer, modname, ispkg in pkgutil.iter_modules( third_party.runtime.impl.__path__ ): if ispkg: potential_runtimes.append(modname) except Exception: # If discovery fails, no third-party runtimes will be loaded potential_runtimes = [] # Try to import each discovered runtime for runtime_name in potential_runtimes: try: module_path = f'{third_party_base}.{runtime_name}.{runtime_name}_runtime' module = importlib.import_module(module_path) # Try different class name patterns possible_class_names = [ f'{runtime_name.upper()}Runtime', # E2BRuntime f'{runtime_name.capitalize()}Runtime', # E2bRuntime, DaytonaRuntime, etc. ] runtime_class = None for class_name in possible_class_names: try: runtime_class = getattr(module, class_name) break except AttributeError: continue if runtime_class: _THIRD_PARTY_RUNTIME_CLASSES[runtime_name] = runtime_class except ImportError: # ImportError means the library is not installed (expected for optional dependencies) pass except Exception as e: # Other exceptions mean the library is present but broken, which should be logged from openhands.core.logger import openhands_logger as logger logger.warning(f'Failed to import third-party runtime {module_path}: {e}') pass except ImportError: # third_party package not available pass # Combine core and third-party runtimes _ALL_RUNTIME_CLASSES = {**_DEFAULT_RUNTIME_CLASSES, **_THIRD_PARTY_RUNTIME_CLASSES} def get_runtime_cls(name: str) -> type[Runtime]: """If name is one of the predefined runtime names (e.g. 'docker'), return its class. Otherwise attempt to resolve name as subclass of Runtime and return it. Raise on invalid selections. """ if name in _ALL_RUNTIME_CLASSES: return _ALL_RUNTIME_CLASSES[name] try: return get_impl(Runtime, name) except Exception as e: known_keys = _ALL_RUNTIME_CLASSES.keys() raise ValueError( f'Runtime {name} not supported, known are: {known_keys}' ) from e # Build __all__ list dynamically based on available runtimes __all__ = [ 'Runtime', 'RemoteRuntime', 'DockerRuntime', 'KubernetesRuntime', 'CLIRuntime', 'LocalRuntime', 'get_runtime_cls', ] # Add third-party runtimes to __all__ if they're available for runtime_name, runtime_class in _THIRD_PARTY_RUNTIME_CLASSES.items(): __all__.append(runtime_class.__name__)