mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-25 21:36:52 +08:00
Remove third-party runtimes (daytona, modal, e2b, runloop) from main codebase (#9213)
Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Engel Nyst <enyst@users.noreply.github.com> Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
This commit is contained in:
parent
6efb992bae
commit
c7dff3e4d2
@ -10,18 +10,7 @@
|
||||
# General core configurations
|
||||
##############################################################################
|
||||
[core]
|
||||
# API key for E2B
|
||||
#e2b_api_key = ""
|
||||
|
||||
# API key for Modal
|
||||
#modal_api_token_id = ""
|
||||
#modal_api_token_secret = ""
|
||||
|
||||
# API key for Daytona
|
||||
#daytona_api_key = ""
|
||||
|
||||
# Daytona Target
|
||||
#daytona_target = ""
|
||||
# API keys and configuration for core services
|
||||
|
||||
# Base path for the workspace
|
||||
#workspace_base = "./workspace"
|
||||
|
||||
@ -3,9 +3,9 @@ repos:
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: ^(docs/|modules/|python/|openhands-ui/)
|
||||
exclude: ^(docs/|modules/|python/|openhands-ui/|third_party/)
|
||||
- id: end-of-file-fixer
|
||||
exclude: ^(docs/|modules/|python/|openhands-ui/)
|
||||
exclude: ^(docs/|modules/|python/|openhands-ui/|third_party/)
|
||||
- id: check-yaml
|
||||
args: ["--allow-multiple-documents"]
|
||||
- id: debug-statements
|
||||
@ -28,10 +28,12 @@ repos:
|
||||
entry: ruff check --config dev_config/python/ruff.toml
|
||||
types_or: [python, pyi, jupyter]
|
||||
args: [--fix, --unsafe-fixes]
|
||||
exclude: third_party/
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
entry: ruff format --config dev_config/python/ruff.toml
|
||||
types_or: [python, pyi, jupyter]
|
||||
exclude: third_party/
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.15.0
|
||||
|
||||
@ -7,3 +7,5 @@ warn_unreachable = True
|
||||
warn_redundant_casts = True
|
||||
no_implicit_optional = True
|
||||
strict_optional = True
|
||||
# Exclude third-party runtime directory from type checking
|
||||
exclude = third_party/
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
# Exclude third-party runtime directory from linting
|
||||
exclude = ["third_party/"]
|
||||
|
||||
[lint]
|
||||
select = [
|
||||
"E",
|
||||
|
||||
@ -12,22 +12,6 @@ description: This page outlines all available configuration options for OpenHand
|
||||
|
||||
The core configuration options are defined in the `[core]` section of the `config.toml` file.
|
||||
|
||||
### API Keys
|
||||
- `e2b_api_key`
|
||||
- Type: `str`
|
||||
- Default: `""`
|
||||
- Description: API key for E2B
|
||||
|
||||
- `modal_api_token_id`
|
||||
- Type: `str`
|
||||
- Default: `""`
|
||||
- Description: API token ID for Modal
|
||||
|
||||
- `modal_api_token_secret`
|
||||
- Type: `str`
|
||||
- Default: `""`
|
||||
- Description: API token secret for Modal
|
||||
|
||||
### Workspace
|
||||
- `workspace_base` **(Deprecated)**
|
||||
- Type: `str`
|
||||
|
||||
@ -9,8 +9,6 @@ commands.
|
||||
By default, OpenHands uses a [Docker-based runtime](/usage/runtimes/docker), running on your local computer.
|
||||
This means you only have to pay for the LLM you're using, and your code is only ever sent to the LLM.
|
||||
|
||||
We also support other runtimes, which are typically managed by third-parties.
|
||||
|
||||
Additionally, we provide a [Local Runtime](/usage/runtimes/local) that runs directly on your machine without Docker,
|
||||
which can be useful in controlled environments like CI pipelines.
|
||||
|
||||
@ -21,6 +19,18 @@ OpenHands supports several different runtime environments:
|
||||
- [Docker Runtime](/usage/runtimes/docker) - The default runtime that uses Docker containers for isolation (recommended for most users).
|
||||
- [OpenHands Remote Runtime](/usage/runtimes/remote) - Cloud-based runtime for parallel execution (beta).
|
||||
- [Local Runtime](/usage/runtimes/local) - Direct execution on your local machine without Docker.
|
||||
- And more third-party runtimes:
|
||||
- [Modal Runtime](/usage/runtimes/modal) - Runtime provided by our partners at Modal.
|
||||
- [Daytona Runtime](/usage/runtimes/daytona) - Runtime provided by Daytona.
|
||||
|
||||
### Third-Party Runtimes
|
||||
|
||||
The following third-party runtimes are available when you install the `third_party_runtimes` extra:
|
||||
|
||||
```bash
|
||||
pip install openhands-ai[third_party_runtimes]
|
||||
```
|
||||
|
||||
- [E2B Runtime](/usage/runtimes/e2b) - Open source runtime using E2B sandboxes.
|
||||
- [Modal Runtime](/usage/runtimes/modal) - Serverless runtime using Modal infrastructure.
|
||||
- [Runloop Runtime](/usage/runtimes/runloop) - Cloud runtime using Runloop infrastructure.
|
||||
- [Daytona Runtime](/usage/runtimes/daytona) - Development environment runtime using Daytona.
|
||||
|
||||
**Note**: These third-party runtimes are supported by their respective developers, not by the OpenHands team. For issues specific to these runtimes, please refer to their documentation or contact their support teams.
|
||||
|
||||
@ -46,7 +46,6 @@ class OpenHandsConfig(BaseModel):
|
||||
run_as_openhands: Whether to run as openhands.
|
||||
max_iterations: Maximum number of iterations allowed.
|
||||
max_budget_per_task: Maximum budget per task, agent stops if exceeded.
|
||||
e2b_api_key: E2B API key.
|
||||
disable_color: Whether to disable terminal colors. For terminals that don't support color.
|
||||
debug: Whether to enable debugging mode.
|
||||
file_uploads_max_file_size_mb: Maximum file upload size in MB. `0` means unlimited.
|
||||
@ -88,19 +87,14 @@ class OpenHandsConfig(BaseModel):
|
||||
run_as_openhands: bool = Field(default=True)
|
||||
max_iterations: int = Field(default=OH_MAX_ITERATIONS)
|
||||
max_budget_per_task: float | None = Field(default=None)
|
||||
e2b_api_key: SecretStr | None = Field(default=None)
|
||||
modal_api_token_id: SecretStr | None = Field(default=None)
|
||||
modal_api_token_secret: SecretStr | None = Field(default=None)
|
||||
|
||||
disable_color: bool = Field(default=False)
|
||||
jwt_secret: SecretStr | None = Field(default=None)
|
||||
debug: bool = Field(default=False)
|
||||
file_uploads_max_file_size_mb: int = Field(default=0)
|
||||
file_uploads_restrict_file_types: bool = Field(default=False)
|
||||
file_uploads_allowed_extensions: list[str] = Field(default_factory=lambda: ['.*'])
|
||||
runloop_api_key: SecretStr | None = Field(default=None)
|
||||
daytona_api_key: SecretStr | None = Field(default=None)
|
||||
daytona_api_url: str = Field(default='https://app.daytona.io/api')
|
||||
daytona_target: str = Field(default='eu')
|
||||
|
||||
cli_multiline_input: bool = Field(default=False)
|
||||
conversation_max_age_seconds: int = Field(default=864000) # 10 days in seconds
|
||||
enable_default_condenser: bool = Field(default=True)
|
||||
|
||||
@ -261,6 +261,7 @@ class SensitiveDataFilter(logging.Filter):
|
||||
'modal_api_token_secret',
|
||||
'llm_api_key',
|
||||
'sandbox_env_github_token',
|
||||
'runloop_api_key',
|
||||
'daytona_api_key',
|
||||
]
|
||||
|
||||
|
||||
@ -1,31 +1,84 @@
|
||||
import importlib
|
||||
|
||||
from openhands.runtime.base import Runtime
|
||||
from openhands.runtime.impl.cli.cli_runtime import CLIRuntime
|
||||
from openhands.runtime.impl.daytona.daytona_runtime import DaytonaRuntime
|
||||
from openhands.runtime.impl.docker.docker_runtime import (
|
||||
DockerRuntime,
|
||||
)
|
||||
from openhands.runtime.impl.e2b.e2b_runtime import E2BRuntime
|
||||
from openhands.runtime.impl.kubernetes.kubernetes_runtime import KubernetesRuntime
|
||||
from openhands.runtime.impl.local.local_runtime import LocalRuntime
|
||||
from openhands.runtime.impl.modal.modal_runtime import ModalRuntime
|
||||
from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime
|
||||
from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime
|
||||
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,
|
||||
'e2b': E2BRuntime,
|
||||
'remote': RemoteRuntime,
|
||||
'modal': ModalRuntime,
|
||||
'runloop': RunloopRuntime,
|
||||
'local': LocalRuntime,
|
||||
'daytona': DaytonaRuntime,
|
||||
'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:
|
||||
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]:
|
||||
"""
|
||||
@ -33,26 +86,28 @@ def get_runtime_cls(name: str) -> type[Runtime]:
|
||||
Otherwise attempt to resolve name as subclass of Runtime and return it.
|
||||
Raise on invalid selections.
|
||||
"""
|
||||
if name in _DEFAULT_RUNTIME_CLASSES:
|
||||
return _DEFAULT_RUNTIME_CLASSES[name]
|
||||
if name in _ALL_RUNTIME_CLASSES:
|
||||
return _ALL_RUNTIME_CLASSES[name]
|
||||
try:
|
||||
return get_impl(Runtime, name)
|
||||
except Exception as e:
|
||||
known_keys = _DEFAULT_RUNTIME_CLASSES.keys()
|
||||
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',
|
||||
'E2BRuntime',
|
||||
'RemoteRuntime',
|
||||
'ModalRuntime',
|
||||
'RunloopRuntime',
|
||||
'DockerRuntime',
|
||||
'DaytonaRuntime',
|
||||
'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__)
|
||||
|
||||
@ -100,11 +100,10 @@ class Runtime(FileEditRuntimeMixin):
|
||||
|
||||
Built-in implementations include:
|
||||
- DockerRuntime: Containerized environment using Docker
|
||||
- E2BRuntime: Secure sandbox using E2B
|
||||
- RemoteRuntime: Remote execution environment
|
||||
- ModalRuntime: Scalable cloud environment using Modal
|
||||
- LocalRuntime: Local execution for development
|
||||
- DaytonaRuntime: Cloud development environment using Daytona
|
||||
- KubernetesRuntime: Kubernetes-based execution environment
|
||||
- CLIRuntime: Command-line interface runtime
|
||||
|
||||
Args:
|
||||
sid: Session ID that uniquely identifies the current user session
|
||||
|
||||
@ -6,22 +6,14 @@ from openhands.runtime.impl.action_execution.action_execution_client import (
|
||||
ActionExecutionClient,
|
||||
)
|
||||
from openhands.runtime.impl.cli import CLIRuntime
|
||||
from openhands.runtime.impl.daytona.daytona_runtime import DaytonaRuntime
|
||||
from openhands.runtime.impl.docker.docker_runtime import DockerRuntime
|
||||
from openhands.runtime.impl.e2b.e2b_runtime import E2BRuntime
|
||||
from openhands.runtime.impl.local.local_runtime import LocalRuntime
|
||||
from openhands.runtime.impl.modal.modal_runtime import ModalRuntime
|
||||
from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime
|
||||
from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime
|
||||
|
||||
__all__ = [
|
||||
'ActionExecutionClient',
|
||||
'CLIRuntime',
|
||||
'DaytonaRuntime',
|
||||
'DockerRuntime',
|
||||
'E2BRuntime',
|
||||
'LocalRuntime',
|
||||
'ModalRuntime',
|
||||
'RemoteRuntime',
|
||||
'RunloopRuntime',
|
||||
]
|
||||
|
||||
67
poetry.lock
generated
67
poetry.lock
generated
@ -1,12 +1,13 @@
|
||||
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aioboto3"
|
||||
version = "14.3.0"
|
||||
description = "Async boto3 wrapper"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = "<4.0,>=3.8"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"},
|
||||
{file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"},
|
||||
@ -24,9 +25,10 @@ s3cse = ["cryptography (>=44.0.1)"]
|
||||
name = "aiobotocore"
|
||||
version = "2.22.0"
|
||||
description = "Async client for aws services using botocore and aiohttp"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"},
|
||||
{file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"},
|
||||
@ -50,9 +52,10 @@ boto3 = ["boto3 (>=1.37.2,<1.37.4)"]
|
||||
name = "aiofiles"
|
||||
version = "24.1.0"
|
||||
description = "File support for asyncio."
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
|
||||
{file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
|
||||
@ -182,9 +185,10 @@ speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>
|
||||
name = "aiohttp-retry"
|
||||
version = "2.9.1"
|
||||
description = "Simple retry client for aiohttp"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"},
|
||||
{file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"},
|
||||
@ -197,9 +201,10 @@ aiohttp = "*"
|
||||
name = "aioitertools"
|
||||
version = "0.12.0"
|
||||
description = "itertools and builtins for AsyncIO and mixed iterables"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"},
|
||||
{file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"},
|
||||
@ -462,7 +467,7 @@ description = "LTS Port of Python audioop"
|
||||
optional = false
|
||||
python-versions = ">=3.13"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.13\""
|
||||
markers = "python_version == \"3.13\""
|
||||
files = [
|
||||
{file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd1345ae99e17e6910f47ce7d52673c6a1a70820d78b67de1b7abb3af29c426a"},
|
||||
{file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:e175350da05d2087e12cea8e72a70a1a8b14a17e92ed2022952a4419689ede5e"},
|
||||
@ -1644,7 +1649,7 @@ files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\" or os_name == \"nt\"", dev = "os_name == \"nt\" or sys_platform == \"win32\"", runtime = "sys_platform == \"win32\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""}
|
||||
markers = {main = "platform_system == \"Windows\" or os_name == \"nt\" or sys_platform == \"win32\"", dev = "os_name == \"nt\" or sys_platform == \"win32\"", runtime = "sys_platform == \"win32\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""}
|
||||
|
||||
[[package]]
|
||||
name = "comm"
|
||||
@ -2000,9 +2005,10 @@ vision = ["Pillow (>=9.4.0)"]
|
||||
name = "daytona"
|
||||
version = "0.21.1"
|
||||
description = "Python SDK for Daytona"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "daytona-0.21.1-py3-none-any.whl", hash = "sha256:1ce6b352f52ef92e667098b7bdaa60c22ffbfb8e686a8cbd12418bf7698ac834"},
|
||||
{file = "daytona-0.21.1.tar.gz", hash = "sha256:01d83dd2b627f87e82491fb97f41845768d75c33f0767eaa44f6e8378bd58e60"},
|
||||
@ -2032,9 +2038,10 @@ dev = ["black[jupyter] (>=23.1.0,<24.0.0)", "build (>=1.0.3)", "isort (>=5.10.0,
|
||||
name = "daytona-api-client"
|
||||
version = "0.21.0"
|
||||
description = "Daytona"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "daytona_api_client-0.21.0-py3-none-any.whl", hash = "sha256:a8ff1f0fb397368dbd6ddb224c28d679e599c657eab2ec5821cf0c972a60229a"},
|
||||
{file = "daytona_api_client-0.21.0.tar.gz", hash = "sha256:92d591c5a1750a827b5850425ce483441609b72b05d35a618d5353fbbba50bca"},
|
||||
@ -2050,9 +2057,10 @@ urllib3 = ">=1.25.3,<3.0.0"
|
||||
name = "daytona-api-client-async"
|
||||
version = "0.21.0"
|
||||
description = "Daytona"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "daytona_api_client_async-0.21.0-py3-none-any.whl", hash = "sha256:f5731963d0dd6c1e207b92bdc7f5b59952d3365444bc9dc8b013d77a4dddf377"},
|
||||
{file = "daytona_api_client_async-0.21.0.tar.gz", hash = "sha256:08a22c0d1616f82efa8d157d7be6c432554fd43d75560725c4e0cef0228607d6"},
|
||||
@ -2317,9 +2325,10 @@ files = [
|
||||
name = "e2b"
|
||||
version = "1.5.2"
|
||||
description = "E2B SDK that give agents cloud environments"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = "<4.0,>=3.9"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "e2b-1.5.2-py3-none-any.whl", hash = "sha256:8cf755f2ff04098daa7ac778f768eee1df730a6181637fe124210345999890b3"},
|
||||
{file = "e2b-1.5.2.tar.gz", hash = "sha256:29ed891ae04ffafff1744c57eff55901200f15030d34ac3fe76d6672e2bf7845"},
|
||||
@ -2349,9 +2358,10 @@ files = [
|
||||
name = "environs"
|
||||
version = "9.5.0"
|
||||
description = "simplified environment variable parsing"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
|
||||
{file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"},
|
||||
@ -3053,8 +3063,8 @@ files = [
|
||||
google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]}
|
||||
google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev"
|
||||
proto-plus = [
|
||||
{version = ">=1.22.3,<2.0.0dev"},
|
||||
{version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0dev"},
|
||||
]
|
||||
protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev"
|
||||
|
||||
@ -3076,8 +3086,8 @@ googleapis-common-protos = ">=1.56.2,<2.0.0"
|
||||
grpcio = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
|
||||
grpcio-status = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
|
||||
proto-plus = [
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
]
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||
requests = ">=2.18.0,<3.0.0"
|
||||
@ -3295,8 +3305,8 @@ google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras
|
||||
google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0"
|
||||
grpc-google-iam-v1 = ">=0.14.0,<1.0.0"
|
||||
proto-plus = [
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
]
|
||||
protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||
|
||||
@ -3648,6 +3658,7 @@ groups = ["main", "evaluation"]
|
||||
files = [
|
||||
{file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
h2 = ">=3.1.0,<5"
|
||||
@ -3710,6 +3721,7 @@ files = [
|
||||
{file = "h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0"},
|
||||
{file = "h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
hpack = ">=4.1,<5"
|
||||
@ -3748,6 +3760,7 @@ files = [
|
||||
{file = "hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496"},
|
||||
{file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[[package]]
|
||||
name = "html2text"
|
||||
@ -3885,6 +3898,7 @@ files = [
|
||||
{file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"},
|
||||
{file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
@ -5389,6 +5403,7 @@ files = [
|
||||
{file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
|
||||
{file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
packaging = ">=17.0"
|
||||
@ -5580,6 +5595,7 @@ files = [
|
||||
{file = "modal-1.0.4-py3-none-any.whl", hash = "sha256:6c0d96bb49b09fa47e407a13e49545e32fe0803803b4330fbeb38de5e71209cc"},
|
||||
{file = "modal-1.0.4.tar.gz", hash = "sha256:09a575ff5fcae1e690b10187bea6da7ff01430c38ec1785090bf7a7ccee7f408"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = "*"
|
||||
@ -6586,8 +6602,8 @@ files = [
|
||||
[package.dependencies]
|
||||
googleapis-common-protos = ">=1.52,<2.0"
|
||||
grpcio = [
|
||||
{version = ">=1.63.2,<2.0.0", markers = "python_version < \"3.13\""},
|
||||
{version = ">=1.66.2,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.63.2,<2.0.0", markers = "python_version < \"3.13\""},
|
||||
]
|
||||
opentelemetry-api = ">=1.15,<2.0"
|
||||
opentelemetry-exporter-otlp-proto-common = "1.34.1"
|
||||
@ -9056,9 +9072,10 @@ files = [
|
||||
name = "runloop-api-client"
|
||||
version = "0.43.0"
|
||||
description = "The official Python library for the runloop API"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"third-party-runtimes\""
|
||||
files = [
|
||||
{file = "runloop_api_client-0.43.0-py3-none-any.whl", hash = "sha256:20b6098b8e0714bb48812a97d5f420f547a98748d52d90789d60a38fa37a2526"},
|
||||
{file = "runloop_api_client-0.43.0.tar.gz", hash = "sha256:879ee6a3baaabd7fd9930fe0c187de8458d138afea4f50c1e428cbf73f2ef08a"},
|
||||
@ -9350,7 +9367,6 @@ files = [
|
||||
{file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"},
|
||||
{file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"},
|
||||
]
|
||||
markers = {evaluation = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""]
|
||||
@ -9442,6 +9458,7 @@ files = [
|
||||
{file = "sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c"},
|
||||
{file = "sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
attrs = "*"
|
||||
@ -9593,7 +9610,7 @@ description = "Standard library aifc redistribution. \"dead battery\"."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.13\""
|
||||
markers = "python_version == \"3.13\""
|
||||
files = [
|
||||
{file = "standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66"},
|
||||
{file = "standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43"},
|
||||
@ -9610,7 +9627,7 @@ description = "Standard library chunk redistribution. \"dead battery\"."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.13\""
|
||||
markers = "python_version == \"3.13\""
|
||||
files = [
|
||||
{file = "standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c"},
|
||||
{file = "standard_chunk-3.13.0.tar.gz", hash = "sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654"},
|
||||
@ -9798,6 +9815,7 @@ files = [
|
||||
{file = "synchronicity-0.9.15-py3-none-any.whl", hash = "sha256:6e3008f54795d73d59fbd133c812734e7c83f4a6f44257cc2a3251237ee8921b"},
|
||||
{file = "synchronicity-0.9.15.tar.gz", hash = "sha256:9451d0caef3509e9f980ba62885a3b8ba7ab247845618e9d9c9c8d11da7ee84b"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
sigtools = ">=4.0.1"
|
||||
@ -10455,6 +10473,7 @@ files = [
|
||||
{file = "types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f"},
|
||||
{file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[[package]]
|
||||
name = "types-python-dateutil"
|
||||
@ -10843,6 +10862,7 @@ files = [
|
||||
{file = "watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0901429650652d3f0da90bad42bdafc1f9143ff3605633c455c999a2d786cac"},
|
||||
{file = "watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9"},
|
||||
]
|
||||
markers = {main = "extra == \"third-party-runtimes\""}
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.0.0"
|
||||
@ -11769,7 +11789,10 @@ cffi = {version = ">=1.11", markers = "platform_python_implementation == \"PyPy\
|
||||
[package.extras]
|
||||
cffi = ["cffi (>=1.11)"]
|
||||
|
||||
[extras]
|
||||
third-party-runtimes = ["daytona", "e2b", "modal", "runloop-api-client"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.12,<3.14"
|
||||
content-hash = "a2cf9e6529f7ed81f96c6183607aa61f99293027bbd3b4a635733f7c3c8e52cb"
|
||||
content-hash = "653c4cda22ec5ff95420d305386c53ba440714fe7c59a9f7f240fdf86b698031"
|
||||
|
||||
@ -14,18 +14,19 @@ readme = "README.md"
|
||||
repository = "https://github.com/All-Hands-AI/OpenHands"
|
||||
packages = [
|
||||
{ include = "openhands/**/*" },
|
||||
{ include = "third_party/**/*" },
|
||||
{ include = "pyproject.toml", to = "openhands" },
|
||||
{ include = "poetry.lock", to = "openhands" },
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12,<3.14"
|
||||
litellm = "^1.60.0, !=1.64.4, !=1.67.*" # avoid 1.64.4 (known bug) & 1.67.* (known bug #10272)
|
||||
aiohttp = ">=3.9.0,!=3.11.13" # Pin to avoid yanked version 3.11.13
|
||||
google-generativeai = "*" # To use litellm with Gemini Pro API
|
||||
google-api-python-client = "^2.164.0" # For Google Sheets API
|
||||
google-auth-httplib2 = "*" # For Google Sheets authentication
|
||||
google-auth-oauthlib = "*" # For Google Sheets OAuth
|
||||
litellm = "^1.60.0, !=1.64.4, !=1.67.*" # avoid 1.64.4 (known bug) & 1.67.* (known bug #10272)
|
||||
aiohttp = ">=3.9.0,!=3.11.13" # Pin to avoid yanked version 3.11.13
|
||||
google-generativeai = "*" # To use litellm with Gemini Pro API
|
||||
google-api-python-client = "^2.164.0" # For Google Sheets API
|
||||
google-auth-httplib2 = "*" # For Google Sheets authentication
|
||||
google-auth-oauthlib = "*" # For Google Sheets OAuth
|
||||
termcolor = "*"
|
||||
docker = "*"
|
||||
fastapi = "*"
|
||||
@ -34,9 +35,9 @@ types-toml = "*"
|
||||
uvicorn = "*"
|
||||
numpy = "*"
|
||||
json-repair = "*"
|
||||
browsergym-core = "0.13.3" # integrate browsergym-core as the browsing interface
|
||||
browsergym-core = "0.13.3" # integrate browsergym-core as the browsing interface
|
||||
html2text = "*"
|
||||
e2b = ">=1.0.5,<1.6.0"
|
||||
|
||||
pexpect = "*"
|
||||
jinja2 = "^3.1.3"
|
||||
python-multipart = "*"
|
||||
@ -52,8 +53,7 @@ whatthepatch = "^1.0.6"
|
||||
protobuf = "^5.0.0,<6.0.0" # Updated to support newer opentelemetry
|
||||
opentelemetry-api = "^1.33.1"
|
||||
opentelemetry-exporter-otlp-proto-grpc = "^1.33.1"
|
||||
modal = ">=0.66.26,<1.1.0"
|
||||
runloop-api-client = "0.43.0"
|
||||
|
||||
libtmux = ">=0.37,<0.40"
|
||||
pygithub = "^2.5.0"
|
||||
joblib = "*"
|
||||
@ -80,7 +80,7 @@ bashlex = "^0.18"
|
||||
# TODO: These are integrations that should probably be optional
|
||||
redis = ">=5.2,<7.0"
|
||||
minio = "^7.2.8"
|
||||
daytona = "0.21.1"
|
||||
|
||||
stripe = ">=11.5,<13.0"
|
||||
google-cloud-aiplatform = "*"
|
||||
anthropic = { extras = [ "vertex" ], version = "*" }
|
||||
@ -88,6 +88,15 @@ boto3 = "*"
|
||||
kubernetes = "^33.1.0"
|
||||
pyyaml = "^6.0.2"
|
||||
|
||||
# Third-party runtime dependencies (optional)
|
||||
e2b = { version = ">=1.0.5,<1.6.0", optional = true }
|
||||
modal = { version = ">=0.66.26,<1.1.0", optional = true }
|
||||
runloop-api-client = { version = "0.43.0", optional = true }
|
||||
daytona = { version = "0.21.1", optional = true }
|
||||
|
||||
[tool.poetry.extras]
|
||||
third_party_runtimes = [ "e2b", "modal", "runloop-api-client", "daytona" ]
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
|
||||
|
||||
@ -12,11 +12,9 @@ from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events import EventStream
|
||||
from openhands.runtime.base import Runtime
|
||||
from openhands.runtime.impl.cli.cli_runtime import CLIRuntime
|
||||
from openhands.runtime.impl.daytona.daytona_runtime import DaytonaRuntime
|
||||
from openhands.runtime.impl.docker.docker_runtime import DockerRuntime
|
||||
from openhands.runtime.impl.local.local_runtime import LocalRuntime
|
||||
from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime
|
||||
from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime
|
||||
from openhands.runtime.plugins import AgentSkillsRequirement, JupyterRequirement
|
||||
from openhands.storage import get_file_store
|
||||
from openhands.utils.async_utils import call_async_from_sync
|
||||
@ -130,10 +128,6 @@ def get_runtime_classes() -> list[type[Runtime]]:
|
||||
return [LocalRuntime]
|
||||
elif runtime.lower() == 'remote':
|
||||
return [RemoteRuntime]
|
||||
elif runtime.lower() == 'runloop':
|
||||
return [RunloopRuntime]
|
||||
elif runtime.lower() == 'daytona':
|
||||
return [DaytonaRuntime]
|
||||
elif runtime.lower() == 'cli':
|
||||
return [CLIRuntime]
|
||||
else:
|
||||
|
||||
@ -990,34 +990,15 @@ def test_api_keys_repr_str():
|
||||
app_config = OpenHandsConfig(
|
||||
llms={'llm': llm_config},
|
||||
agents={'agent': agent_config},
|
||||
e2b_api_key='my_e2b_api_key',
|
||||
jwt_secret='my_jwt_secret',
|
||||
modal_api_token_id='my_modal_api_token_id',
|
||||
modal_api_token_secret='my_modal_api_token_secret',
|
||||
runloop_api_key='my_runloop_api_key',
|
||||
daytona_api_key='my_daytona_api_key',
|
||||
search_api_key='my_search_api_key',
|
||||
)
|
||||
assert 'my_e2b_api_key' not in repr(app_config)
|
||||
assert 'my_e2b_api_key' not in str(app_config)
|
||||
assert 'my_jwt_secret' not in repr(app_config)
|
||||
assert 'my_jwt_secret' not in str(app_config)
|
||||
assert 'my_modal_api_token_id' not in repr(app_config)
|
||||
assert 'my_modal_api_token_id' not in str(app_config)
|
||||
assert 'my_modal_api_token_secret' not in repr(app_config)
|
||||
assert 'my_modal_api_token_secret' not in str(app_config)
|
||||
assert 'my_runloop_api_key' not in repr(app_config)
|
||||
assert 'my_runloop_api_key' not in str(app_config)
|
||||
assert 'my_daytona_api_key' not in repr(app_config)
|
||||
assert 'my_daytona_api_key' not in str(app_config)
|
||||
|
||||
assert 'my_search_api_key' not in repr(app_config)
|
||||
assert 'my_search_api_key' not in str(app_config)
|
||||
|
||||
# Check that no other attrs in OpenHandsConfig have 'key' or 'token' in their name
|
||||
# This will fail when new attrs are added, and attract attention
|
||||
known_key_token_attrs_app = [
|
||||
'e2b_api_key',
|
||||
'modal_api_token_id',
|
||||
'modal_api_token_secret',
|
||||
'runloop_api_key',
|
||||
'daytona_api_key',
|
||||
'search_api_key',
|
||||
]
|
||||
for attr_name in OpenHandsConfig.model_fields.keys():
|
||||
|
||||
@ -84,11 +84,11 @@ def test_llm_config_attributes_masking(test_handler):
|
||||
|
||||
def test_app_config_attributes_masking(test_handler):
|
||||
logger, stream = test_handler
|
||||
app_config = OpenHandsConfig(e2b_api_key='e2b-xyz789')
|
||||
app_config = OpenHandsConfig(search_api_key='search-xyz789')
|
||||
logger.info(f'App Config: {app_config}')
|
||||
log_output = stream.getvalue()
|
||||
assert 'github_token' not in log_output
|
||||
assert 'e2b-xyz789' not in log_output
|
||||
assert 'search-xyz789' not in log_output
|
||||
assert 'ghp_abcdefghijklmnopqrstuvwxyz' not in log_output
|
||||
|
||||
|
||||
|
||||
14
third_party/__init__.py
vendored
Normal file
14
third_party/__init__.py
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
"""Third-party runtime implementations for OpenHands.
|
||||
|
||||
This module contains runtime implementations provided by third-party vendors.
|
||||
These runtimes are optional and require additional dependencies to be installed.
|
||||
|
||||
To use third-party runtimes, install OpenHands with the third_party_runtimes extra:
|
||||
pip install openhands-ai[third_party_runtimes]
|
||||
|
||||
Available third-party runtimes:
|
||||
- daytona: Daytona cloud development environment
|
||||
- e2b: E2B secure sandbox environment
|
||||
- modal: Modal cloud compute platform
|
||||
- runloop: Runloop AI sandbox environment
|
||||
"""
|
||||
1
third_party/runtime/__init__.py
vendored
Normal file
1
third_party/runtime/__init__.py
vendored
Normal file
@ -0,0 +1 @@
|
||||
"""Third-party runtime implementations."""
|
||||
1
third_party/runtime/impl/__init__.py
vendored
Normal file
1
third_party/runtime/impl/__init__.py
vendored
Normal file
@ -0,0 +1 @@
|
||||
"""Third-party runtime implementation modules."""
|
||||
7
third_party/runtime/impl/daytona/__init__.py
vendored
Normal file
7
third_party/runtime/impl/daytona/__init__.py
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
"""Daytona runtime implementation.
|
||||
|
||||
This runtime reads configuration directly from environment variables:
|
||||
- DAYTONA_API_KEY: API key for Daytona authentication
|
||||
- DAYTONA_API_URL: Daytona API URL endpoint (defaults to https://app.daytona.io/api)
|
||||
- DAYTONA_TARGET: Daytona target region (defaults to 'eu')
|
||||
"""
|
||||
@ -1,3 +1,4 @@
|
||||
import os
|
||||
from typing import Callable
|
||||
|
||||
import httpx
|
||||
@ -45,7 +46,13 @@ class DaytonaRuntime(ActionExecutionClient):
|
||||
user_id: str | None = None,
|
||||
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
|
||||
):
|
||||
assert config.daytona_api_key, 'Daytona API key is required'
|
||||
# Read Daytona configuration from environment variables
|
||||
daytona_api_key = os.getenv('DAYTONA_API_KEY')
|
||||
if not daytona_api_key:
|
||||
raise ValueError('DAYTONA_API_KEY environment variable is required for Daytona runtime')
|
||||
|
||||
daytona_api_url = os.getenv('DAYTONA_API_URL', 'https://app.daytona.io/api')
|
||||
daytona_target = os.getenv('DAYTONA_TARGET', 'eu')
|
||||
|
||||
self.config = config
|
||||
self.sid = sid
|
||||
@ -53,9 +60,9 @@ class DaytonaRuntime(ActionExecutionClient):
|
||||
self._vscode_url: str | None = None
|
||||
|
||||
daytona_config = DaytonaConfig(
|
||||
api_key=config.daytona_api_key.get_secret_value(),
|
||||
server_url=config.daytona_api_url,
|
||||
target=config.daytona_target,
|
||||
api_key=daytona_api_key,
|
||||
server_url=daytona_api_url,
|
||||
target=daytona_target,
|
||||
)
|
||||
self.daytona = Daytona(daytona_config)
|
||||
|
||||
5
third_party/runtime/impl/e2b/__init__.py
vendored
Normal file
5
third_party/runtime/impl/e2b/__init__.py
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
"""E2B runtime implementation.
|
||||
|
||||
This runtime reads configuration directly from environment variables:
|
||||
- E2B_API_KEY: API key for E2B authentication
|
||||
"""
|
||||
@ -16,8 +16,8 @@ from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
|
||||
from openhands.runtime.impl.action_execution.action_execution_client import (
|
||||
ActionExecutionClient,
|
||||
)
|
||||
from openhands.runtime.impl.e2b.filestore import E2BFileStore
|
||||
from openhands.runtime.impl.e2b.sandbox import E2BSandbox
|
||||
from third_party.runtime.impl.e2b.filestore import E2BFileStore
|
||||
from third_party.runtime.impl.e2b.sandbox import E2BSandbox
|
||||
from openhands.runtime.plugins import PluginRequirement
|
||||
from openhands.runtime.utils.files import insert_lines, read_lines
|
||||
|
||||
@ -50,7 +50,7 @@ class E2BRuntime(ActionExecutionClient):
|
||||
git_provider_tokens,
|
||||
)
|
||||
if sandbox is None:
|
||||
self.sandbox = E2BSandbox()
|
||||
self.sandbox = E2BSandbox(config.sandbox)
|
||||
if not isinstance(self.sandbox, E2BSandbox):
|
||||
raise ValueError('E2BRuntime requires an E2BSandbox')
|
||||
self.file_store = E2BFileStore(self.sandbox.filesystem)
|
||||
@ -19,11 +19,16 @@ class E2BBox:
|
||||
def __init__(
|
||||
self,
|
||||
config: SandboxConfig,
|
||||
e2b_api_key: str,
|
||||
template: str = 'openhands',
|
||||
):
|
||||
self.config = copy.deepcopy(config)
|
||||
self.initialize_plugins: bool = config.initialize_plugins
|
||||
|
||||
# Read API key from environment variable
|
||||
e2b_api_key = os.getenv('E2B_API_KEY')
|
||||
if not e2b_api_key:
|
||||
raise ValueError('E2B_API_KEY environment variable is required for E2B runtime')
|
||||
|
||||
self.sandbox = E2BSandbox(
|
||||
api_key=e2b_api_key,
|
||||
template=template,
|
||||
@ -112,3 +117,7 @@ class E2BBox:
|
||||
|
||||
def get_working_directory(self):
|
||||
return self.sandbox.cwd
|
||||
|
||||
|
||||
# Alias for backward compatibility
|
||||
E2BSandbox = E2BBox
|
||||
6
third_party/runtime/impl/modal/__init__.py
vendored
Normal file
6
third_party/runtime/impl/modal/__init__.py
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
"""Modal runtime implementation.
|
||||
|
||||
This runtime reads configuration directly from environment variables:
|
||||
- MODAL_TOKEN_ID: Modal API token ID for authentication
|
||||
- MODAL_TOKEN_SECRET: Modal API token secret for authentication
|
||||
"""
|
||||
@ -57,16 +57,22 @@ class ModalRuntime(ActionExecutionClient):
|
||||
user_id: str | None = None,
|
||||
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
|
||||
):
|
||||
assert config.modal_api_token_id, 'Modal API token id is required'
|
||||
assert config.modal_api_token_secret, 'Modal API token secret is required'
|
||||
# Read Modal API credentials from environment variables
|
||||
modal_token_id = os.getenv('MODAL_TOKEN_ID')
|
||||
modal_token_secret = os.getenv('MODAL_TOKEN_SECRET')
|
||||
|
||||
if not modal_token_id:
|
||||
raise ValueError('MODAL_TOKEN_ID environment variable is required for Modal runtime')
|
||||
if not modal_token_secret:
|
||||
raise ValueError('MODAL_TOKEN_SECRET environment variable is required for Modal runtime')
|
||||
|
||||
self.config = config
|
||||
self.sandbox = None
|
||||
self.sid = sid
|
||||
|
||||
self.modal_client = modal.Client.from_credentials(
|
||||
config.modal_api_token_id.get_secret_value(),
|
||||
config.modal_api_token_secret.get_secret_value(),
|
||||
modal_token_id,
|
||||
modal_token_secret,
|
||||
)
|
||||
self.app = modal.App.lookup(
|
||||
'openhands', create_if_missing=True, client=self.modal_client
|
||||
5
third_party/runtime/impl/runloop/__init__.py
vendored
Normal file
5
third_party/runtime/impl/runloop/__init__.py
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
"""Runloop runtime implementation.
|
||||
|
||||
This runtime reads configuration directly from environment variables:
|
||||
- RUNLOOP_API_KEY: API key for Runloop authentication
|
||||
"""
|
||||
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import os
|
||||
from typing import Callable
|
||||
|
||||
import tenacity
|
||||
@ -40,11 +41,15 @@ class RunloopRuntime(ActionExecutionClient):
|
||||
user_id: str | None = None,
|
||||
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
|
||||
):
|
||||
assert config.runloop_api_key is not None, 'Runloop API key is required'
|
||||
# Read Runloop API key from environment variable
|
||||
runloop_api_key = os.getenv('RUNLOOP_API_KEY')
|
||||
if not runloop_api_key:
|
||||
raise ValueError('RUNLOOP_API_KEY environment variable is required for Runloop runtime')
|
||||
|
||||
self.devbox: DevboxView | None = None
|
||||
self.config = config
|
||||
self.runloop_api_client = Runloop(
|
||||
bearer_token=config.runloop_api_key.get_secret_value(),
|
||||
bearer_token=runloop_api_key,
|
||||
)
|
||||
self.container_name = CONTAINER_NAME_PREFIX + sid
|
||||
super().__init__(
|
||||
Loading…
x
Reference in New Issue
Block a user