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:
Graham Neubig 2025-06-26 07:39:39 -04:00 committed by GitHub
parent 6efb992bae
commit c7dff3e4d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 251 additions and 147 deletions

View File

@ -10,18 +10,7 @@
# General core configurations # General core configurations
############################################################################## ##############################################################################
[core] [core]
# API key for E2B # API keys and configuration for core services
#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 = ""
# Base path for the workspace # Base path for the workspace
#workspace_base = "./workspace" #workspace_base = "./workspace"

View File

@ -3,9 +3,9 @@ repos:
rev: v5.0.0 rev: v5.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
exclude: ^(docs/|modules/|python/|openhands-ui/) exclude: ^(docs/|modules/|python/|openhands-ui/|third_party/)
- id: end-of-file-fixer - id: end-of-file-fixer
exclude: ^(docs/|modules/|python/|openhands-ui/) exclude: ^(docs/|modules/|python/|openhands-ui/|third_party/)
- id: check-yaml - id: check-yaml
args: ["--allow-multiple-documents"] args: ["--allow-multiple-documents"]
- id: debug-statements - id: debug-statements
@ -28,10 +28,12 @@ repos:
entry: ruff check --config dev_config/python/ruff.toml entry: ruff check --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter] types_or: [python, pyi, jupyter]
args: [--fix, --unsafe-fixes] args: [--fix, --unsafe-fixes]
exclude: third_party/
# Run the formatter. # Run the formatter.
- id: ruff-format - id: ruff-format
entry: ruff format --config dev_config/python/ruff.toml entry: ruff format --config dev_config/python/ruff.toml
types_or: [python, pyi, jupyter] types_or: [python, pyi, jupyter]
exclude: third_party/
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0 rev: v1.15.0

View File

@ -7,3 +7,5 @@ warn_unreachable = True
warn_redundant_casts = True warn_redundant_casts = True
no_implicit_optional = True no_implicit_optional = True
strict_optional = True strict_optional = True
# Exclude third-party runtime directory from type checking
exclude = third_party/

View File

@ -1,3 +1,6 @@
# Exclude third-party runtime directory from linting
exclude = ["third_party/"]
[lint] [lint]
select = [ select = [
"E", "E",

View File

@ -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. 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
- `workspace_base` **(Deprecated)** - `workspace_base` **(Deprecated)**
- Type: `str` - Type: `str`

View File

@ -9,8 +9,6 @@ commands.
By default, OpenHands uses a [Docker-based runtime](/usage/runtimes/docker), running on your local computer. 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. 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, 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. 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). - [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). - [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. - [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. ### Third-Party Runtimes
- [Daytona Runtime](/usage/runtimes/daytona) - Runtime provided by Daytona.
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.

View File

@ -46,7 +46,6 @@ class OpenHandsConfig(BaseModel):
run_as_openhands: Whether to run as openhands. run_as_openhands: Whether to run as openhands.
max_iterations: Maximum number of iterations allowed. max_iterations: Maximum number of iterations allowed.
max_budget_per_task: Maximum budget per task, agent stops if exceeded. 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. disable_color: Whether to disable terminal colors. For terminals that don't support color.
debug: Whether to enable debugging mode. debug: Whether to enable debugging mode.
file_uploads_max_file_size_mb: Maximum file upload size in MB. `0` means unlimited. 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) run_as_openhands: bool = Field(default=True)
max_iterations: int = Field(default=OH_MAX_ITERATIONS) max_iterations: int = Field(default=OH_MAX_ITERATIONS)
max_budget_per_task: float | None = Field(default=None) 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) disable_color: bool = Field(default=False)
jwt_secret: SecretStr | None = Field(default=None) jwt_secret: SecretStr | None = Field(default=None)
debug: bool = Field(default=False) debug: bool = Field(default=False)
file_uploads_max_file_size_mb: int = Field(default=0) file_uploads_max_file_size_mb: int = Field(default=0)
file_uploads_restrict_file_types: bool = Field(default=False) file_uploads_restrict_file_types: bool = Field(default=False)
file_uploads_allowed_extensions: list[str] = Field(default_factory=lambda: ['.*']) 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) cli_multiline_input: bool = Field(default=False)
conversation_max_age_seconds: int = Field(default=864000) # 10 days in seconds conversation_max_age_seconds: int = Field(default=864000) # 10 days in seconds
enable_default_condenser: bool = Field(default=True) enable_default_condenser: bool = Field(default=True)

View File

@ -261,6 +261,7 @@ class SensitiveDataFilter(logging.Filter):
'modal_api_token_secret', 'modal_api_token_secret',
'llm_api_key', 'llm_api_key',
'sandbox_env_github_token', 'sandbox_env_github_token',
'runloop_api_key',
'daytona_api_key', 'daytona_api_key',
] ]

View File

@ -1,31 +1,84 @@
import importlib
from openhands.runtime.base import Runtime from openhands.runtime.base import Runtime
from openhands.runtime.impl.cli.cli_runtime import CLIRuntime 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 ( from openhands.runtime.impl.docker.docker_runtime import (
DockerRuntime, DockerRuntime,
) )
from openhands.runtime.impl.e2b.e2b_runtime import E2BRuntime
from openhands.runtime.impl.kubernetes.kubernetes_runtime import KubernetesRuntime from openhands.runtime.impl.kubernetes.kubernetes_runtime import KubernetesRuntime
from openhands.runtime.impl.local.local_runtime import LocalRuntime 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.remote.remote_runtime import RemoteRuntime
from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime
from openhands.utils.import_utils import get_impl from openhands.utils.import_utils import get_impl
# mypy: disable-error-code="type-abstract" # mypy: disable-error-code="type-abstract"
_DEFAULT_RUNTIME_CLASSES: dict[str, type[Runtime]] = { _DEFAULT_RUNTIME_CLASSES: dict[str, type[Runtime]] = {
'eventstream': DockerRuntime, 'eventstream': DockerRuntime,
'docker': DockerRuntime, 'docker': DockerRuntime,
'e2b': E2BRuntime,
'remote': RemoteRuntime, 'remote': RemoteRuntime,
'modal': ModalRuntime,
'runloop': RunloopRuntime,
'local': LocalRuntime, 'local': LocalRuntime,
'daytona': DaytonaRuntime,
'kubernetes': KubernetesRuntime, 'kubernetes': KubernetesRuntime,
'cli': CLIRuntime, '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]: 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. Otherwise attempt to resolve name as subclass of Runtime and return it.
Raise on invalid selections. Raise on invalid selections.
""" """
if name in _DEFAULT_RUNTIME_CLASSES: if name in _ALL_RUNTIME_CLASSES:
return _DEFAULT_RUNTIME_CLASSES[name] return _ALL_RUNTIME_CLASSES[name]
try: try:
return get_impl(Runtime, name) return get_impl(Runtime, name)
except Exception as e: except Exception as e:
known_keys = _DEFAULT_RUNTIME_CLASSES.keys() known_keys = _ALL_RUNTIME_CLASSES.keys()
raise ValueError( raise ValueError(
f'Runtime {name} not supported, known are: {known_keys}' f'Runtime {name} not supported, known are: {known_keys}'
) from e ) from e
# Build __all__ list dynamically based on available runtimes
__all__ = [ __all__ = [
'Runtime', 'Runtime',
'E2BRuntime',
'RemoteRuntime', 'RemoteRuntime',
'ModalRuntime',
'RunloopRuntime',
'DockerRuntime', 'DockerRuntime',
'DaytonaRuntime',
'KubernetesRuntime', 'KubernetesRuntime',
'CLIRuntime', 'CLIRuntime',
'LocalRuntime',
'get_runtime_cls', '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__)

View File

@ -100,11 +100,10 @@ class Runtime(FileEditRuntimeMixin):
Built-in implementations include: Built-in implementations include:
- DockerRuntime: Containerized environment using Docker - DockerRuntime: Containerized environment using Docker
- E2BRuntime: Secure sandbox using E2B
- RemoteRuntime: Remote execution environment - RemoteRuntime: Remote execution environment
- ModalRuntime: Scalable cloud environment using Modal
- LocalRuntime: Local execution for development - LocalRuntime: Local execution for development
- DaytonaRuntime: Cloud development environment using Daytona - KubernetesRuntime: Kubernetes-based execution environment
- CLIRuntime: Command-line interface runtime
Args: Args:
sid: Session ID that uniquely identifies the current user session sid: Session ID that uniquely identifies the current user session

View File

@ -6,22 +6,14 @@ from openhands.runtime.impl.action_execution.action_execution_client import (
ActionExecutionClient, ActionExecutionClient,
) )
from openhands.runtime.impl.cli import CLIRuntime 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.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.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.remote.remote_runtime import RemoteRuntime
from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime
__all__ = [ __all__ = [
'ActionExecutionClient', 'ActionExecutionClient',
'CLIRuntime', 'CLIRuntime',
'DaytonaRuntime',
'DockerRuntime', 'DockerRuntime',
'E2BRuntime',
'LocalRuntime', 'LocalRuntime',
'ModalRuntime',
'RemoteRuntime', 'RemoteRuntime',
'RunloopRuntime',
] ]

67
poetry.lock generated
View File

@ -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]] [[package]]
name = "aioboto3" name = "aioboto3"
version = "14.3.0" version = "14.3.0"
description = "Async boto3 wrapper" description = "Async boto3 wrapper"
optional = false optional = true
python-versions = "<4.0,>=3.8" python-versions = "<4.0,>=3.8"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"}, {file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"},
{file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"}, {file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"},
@ -24,9 +25,10 @@ s3cse = ["cryptography (>=44.0.1)"]
name = "aiobotocore" name = "aiobotocore"
version = "2.22.0" version = "2.22.0"
description = "Async client for aws services using botocore and aiohttp" description = "Async client for aws services using botocore and aiohttp"
optional = false optional = true
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"}, {file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"},
{file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"}, {file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"},
@ -50,9 +52,10 @@ boto3 = ["boto3 (>=1.37.2,<1.37.4)"]
name = "aiofiles" name = "aiofiles"
version = "24.1.0" version = "24.1.0"
description = "File support for asyncio." description = "File support for asyncio."
optional = false optional = true
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
{file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
@ -182,9 +185,10 @@ speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>
name = "aiohttp-retry" name = "aiohttp-retry"
version = "2.9.1" version = "2.9.1"
description = "Simple retry client for aiohttp" description = "Simple retry client for aiohttp"
optional = false optional = true
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"}, {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"},
{file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"}, {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"},
@ -197,9 +201,10 @@ aiohttp = "*"
name = "aioitertools" name = "aioitertools"
version = "0.12.0" version = "0.12.0"
description = "itertools and builtins for AsyncIO and mixed iterables" description = "itertools and builtins for AsyncIO and mixed iterables"
optional = false optional = true
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"},
{file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"},
@ -462,7 +467,7 @@ description = "LTS Port of Python audioop"
optional = false optional = false
python-versions = ">=3.13" python-versions = ">=3.13"
groups = ["main"] groups = ["main"]
markers = "python_version >= \"3.13\"" markers = "python_version == \"3.13\""
files = [ 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_universal2.whl", hash = "sha256:fd1345ae99e17e6910f47ce7d52673c6a1a70820d78b67de1b7abb3af29c426a"},
{file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:e175350da05d2087e12cea8e72a70a1a8b14a17e92ed2022952a4419689ede5e"}, {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-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {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]] [[package]]
name = "comm" name = "comm"
@ -2000,9 +2005,10 @@ vision = ["Pillow (>=9.4.0)"]
name = "daytona" name = "daytona"
version = "0.21.1" version = "0.21.1"
description = "Python SDK for Daytona" description = "Python SDK for Daytona"
optional = false optional = true
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "daytona-0.21.1-py3-none-any.whl", hash = "sha256:1ce6b352f52ef92e667098b7bdaa60c22ffbfb8e686a8cbd12418bf7698ac834"}, {file = "daytona-0.21.1-py3-none-any.whl", hash = "sha256:1ce6b352f52ef92e667098b7bdaa60c22ffbfb8e686a8cbd12418bf7698ac834"},
{file = "daytona-0.21.1.tar.gz", hash = "sha256:01d83dd2b627f87e82491fb97f41845768d75c33f0767eaa44f6e8378bd58e60"}, {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" name = "daytona-api-client"
version = "0.21.0" version = "0.21.0"
description = "Daytona" description = "Daytona"
optional = false optional = true
python-versions = "*" python-versions = "*"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "daytona_api_client-0.21.0-py3-none-any.whl", hash = "sha256:a8ff1f0fb397368dbd6ddb224c28d679e599c657eab2ec5821cf0c972a60229a"}, {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"}, {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" name = "daytona-api-client-async"
version = "0.21.0" version = "0.21.0"
description = "Daytona" description = "Daytona"
optional = false optional = true
python-versions = "*" python-versions = "*"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "daytona_api_client_async-0.21.0-py3-none-any.whl", hash = "sha256:f5731963d0dd6c1e207b92bdc7f5b59952d3365444bc9dc8b013d77a4dddf377"}, {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"}, {file = "daytona_api_client_async-0.21.0.tar.gz", hash = "sha256:08a22c0d1616f82efa8d157d7be6c432554fd43d75560725c4e0cef0228607d6"},
@ -2317,9 +2325,10 @@ files = [
name = "e2b" name = "e2b"
version = "1.5.2" version = "1.5.2"
description = "E2B SDK that give agents cloud environments" description = "E2B SDK that give agents cloud environments"
optional = false optional = true
python-versions = "<4.0,>=3.9" python-versions = "<4.0,>=3.9"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "e2b-1.5.2-py3-none-any.whl", hash = "sha256:8cf755f2ff04098daa7ac778f768eee1df730a6181637fe124210345999890b3"}, {file = "e2b-1.5.2-py3-none-any.whl", hash = "sha256:8cf755f2ff04098daa7ac778f768eee1df730a6181637fe124210345999890b3"},
{file = "e2b-1.5.2.tar.gz", hash = "sha256:29ed891ae04ffafff1744c57eff55901200f15030d34ac3fe76d6672e2bf7845"}, {file = "e2b-1.5.2.tar.gz", hash = "sha256:29ed891ae04ffafff1744c57eff55901200f15030d34ac3fe76d6672e2bf7845"},
@ -2349,9 +2358,10 @@ files = [
name = "environs" name = "environs"
version = "9.5.0" version = "9.5.0"
description = "simplified environment variable parsing" description = "simplified environment variable parsing"
optional = false optional = true
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"}, {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
{file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"}, {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-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" google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev"
proto-plus = [ proto-plus = [
{version = ">=1.22.3,<2.0.0dev"},
{version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, {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" 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 = {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\""} grpcio-status = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
proto-plus = [ proto-plus = [
{version = ">=1.22.3,<2.0.0"},
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, {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" 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" 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" 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" grpc-google-iam-v1 = ">=0.14.0,<1.0.0"
proto-plus = [ proto-plus = [
{version = ">=1.22.3,<2.0.0"},
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, {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" 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 = [ files = [
{file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"}, {file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
h2 = ">=3.1.0,<5" 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-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0"},
{file = "h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f"}, {file = "h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
hpack = ">=4.1,<5" 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-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496"},
{file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"}, {file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[[package]] [[package]]
name = "html2text" name = "html2text"
@ -3885,6 +3898,7 @@ files = [
{file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"}, {file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"},
{file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"}, {file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[[package]] [[package]]
name = "identify" name = "identify"
@ -5389,6 +5403,7 @@ files = [
{file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
{file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
packaging = ">=17.0" 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-py3-none-any.whl", hash = "sha256:6c0d96bb49b09fa47e407a13e49545e32fe0803803b4330fbeb38de5e71209cc"},
{file = "modal-1.0.4.tar.gz", hash = "sha256:09a575ff5fcae1e690b10187bea6da7ff01430c38ec1785090bf7a7ccee7f408"}, {file = "modal-1.0.4.tar.gz", hash = "sha256:09a575ff5fcae1e690b10187bea6da7ff01430c38ec1785090bf7a7ccee7f408"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
aiohttp = "*" aiohttp = "*"
@ -6586,8 +6602,8 @@ files = [
[package.dependencies] [package.dependencies]
googleapis-common-protos = ">=1.52,<2.0" googleapis-common-protos = ">=1.52,<2.0"
grpcio = [ 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.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-api = ">=1.15,<2.0"
opentelemetry-exporter-otlp-proto-common = "1.34.1" opentelemetry-exporter-otlp-proto-common = "1.34.1"
@ -9056,9 +9072,10 @@ files = [
name = "runloop-api-client" name = "runloop-api-client"
version = "0.43.0" version = "0.43.0"
description = "The official Python library for the runloop API" description = "The official Python library for the runloop API"
optional = false optional = true
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"] groups = ["main"]
markers = "extra == \"third-party-runtimes\""
files = [ files = [
{file = "runloop_api_client-0.43.0-py3-none-any.whl", hash = "sha256:20b6098b8e0714bb48812a97d5f420f547a98748d52d90789d60a38fa37a2526"}, {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"}, {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-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"},
{file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"},
] ]
markers = {evaluation = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
[package.extras] [package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] 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-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c"},
{file = "sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c"}, {file = "sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
attrs = "*" attrs = "*"
@ -9593,7 +9610,7 @@ description = "Standard library aifc redistribution. \"dead battery\"."
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["main"] groups = ["main"]
markers = "python_version >= \"3.13\"" markers = "python_version == \"3.13\""
files = [ files = [
{file = "standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66"}, {file = "standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66"},
{file = "standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43"}, {file = "standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43"},
@ -9610,7 +9627,7 @@ description = "Standard library chunk redistribution. \"dead battery\"."
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["main"] groups = ["main"]
markers = "python_version >= \"3.13\"" markers = "python_version == \"3.13\""
files = [ files = [
{file = "standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c"}, {file = "standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c"},
{file = "standard_chunk-3.13.0.tar.gz", hash = "sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654"}, {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-py3-none-any.whl", hash = "sha256:6e3008f54795d73d59fbd133c812734e7c83f4a6f44257cc2a3251237ee8921b"},
{file = "synchronicity-0.9.15.tar.gz", hash = "sha256:9451d0caef3509e9f980ba62885a3b8ba7ab247845618e9d9c9c8d11da7ee84b"}, {file = "synchronicity-0.9.15.tar.gz", hash = "sha256:9451d0caef3509e9f980ba62885a3b8ba7ab247845618e9d9c9c8d11da7ee84b"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
sigtools = ">=4.0.1" 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.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f"},
{file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"}, {file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[[package]] [[package]]
name = "types-python-dateutil" 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-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"}, {file = "watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9"},
] ]
markers = {main = "extra == \"third-party-runtimes\""}
[package.dependencies] [package.dependencies]
anyio = ">=3.0.0" anyio = ">=3.0.0"
@ -11769,7 +11789,10 @@ cffi = {version = ">=1.11", markers = "platform_python_implementation == \"PyPy\
[package.extras] [package.extras]
cffi = ["cffi (>=1.11)"] cffi = ["cffi (>=1.11)"]
[extras]
third-party-runtimes = ["daytona", "e2b", "modal", "runloop-api-client"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.12,<3.14" python-versions = "^3.12,<3.14"
content-hash = "a2cf9e6529f7ed81f96c6183607aa61f99293027bbd3b4a635733f7c3c8e52cb" content-hash = "653c4cda22ec5ff95420d305386c53ba440714fe7c59a9f7f240fdf86b698031"

View File

@ -14,18 +14,19 @@ readme = "README.md"
repository = "https://github.com/All-Hands-AI/OpenHands" repository = "https://github.com/All-Hands-AI/OpenHands"
packages = [ packages = [
{ include = "openhands/**/*" }, { include = "openhands/**/*" },
{ include = "third_party/**/*" },
{ include = "pyproject.toml", to = "openhands" }, { include = "pyproject.toml", to = "openhands" },
{ include = "poetry.lock", to = "openhands" }, { include = "poetry.lock", to = "openhands" },
] ]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.12,<3.14" 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) 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 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-generativeai = "*" # To use litellm with Gemini Pro API
google-api-python-client = "^2.164.0" # For Google Sheets API google-api-python-client = "^2.164.0" # For Google Sheets API
google-auth-httplib2 = "*" # For Google Sheets authentication google-auth-httplib2 = "*" # For Google Sheets authentication
google-auth-oauthlib = "*" # For Google Sheets OAuth google-auth-oauthlib = "*" # For Google Sheets OAuth
termcolor = "*" termcolor = "*"
docker = "*" docker = "*"
fastapi = "*" fastapi = "*"
@ -34,9 +35,9 @@ types-toml = "*"
uvicorn = "*" uvicorn = "*"
numpy = "*" numpy = "*"
json-repair = "*" 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 = "*" html2text = "*"
e2b = ">=1.0.5,<1.6.0"
pexpect = "*" pexpect = "*"
jinja2 = "^3.1.3" jinja2 = "^3.1.3"
python-multipart = "*" python-multipart = "*"
@ -52,8 +53,7 @@ whatthepatch = "^1.0.6"
protobuf = "^5.0.0,<6.0.0" # Updated to support newer opentelemetry protobuf = "^5.0.0,<6.0.0" # Updated to support newer opentelemetry
opentelemetry-api = "^1.33.1" opentelemetry-api = "^1.33.1"
opentelemetry-exporter-otlp-proto-grpc = "^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" libtmux = ">=0.37,<0.40"
pygithub = "^2.5.0" pygithub = "^2.5.0"
joblib = "*" joblib = "*"
@ -80,7 +80,7 @@ bashlex = "^0.18"
# TODO: These are integrations that should probably be optional # TODO: These are integrations that should probably be optional
redis = ">=5.2,<7.0" redis = ">=5.2,<7.0"
minio = "^7.2.8" minio = "^7.2.8"
daytona = "0.21.1"
stripe = ">=11.5,<13.0" stripe = ">=11.5,<13.0"
google-cloud-aiplatform = "*" google-cloud-aiplatform = "*"
anthropic = { extras = [ "vertex" ], version = "*" } anthropic = { extras = [ "vertex" ], version = "*" }
@ -88,6 +88,15 @@ boto3 = "*"
kubernetes = "^33.1.0" kubernetes = "^33.1.0"
pyyaml = "^6.0.2" 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] [tool.poetry.group.dev]
optional = true optional = true

View File

@ -12,11 +12,9 @@ from openhands.core.logger import openhands_logger as logger
from openhands.events import EventStream from openhands.events import EventStream
from openhands.runtime.base import Runtime from openhands.runtime.base import Runtime
from openhands.runtime.impl.cli.cli_runtime import CLIRuntime 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.docker.docker_runtime import DockerRuntime
from openhands.runtime.impl.local.local_runtime import LocalRuntime from openhands.runtime.impl.local.local_runtime import LocalRuntime
from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime 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.runtime.plugins import AgentSkillsRequirement, JupyterRequirement
from openhands.storage import get_file_store from openhands.storage import get_file_store
from openhands.utils.async_utils import call_async_from_sync from openhands.utils.async_utils import call_async_from_sync
@ -130,10 +128,6 @@ def get_runtime_classes() -> list[type[Runtime]]:
return [LocalRuntime] return [LocalRuntime]
elif runtime.lower() == 'remote': elif runtime.lower() == 'remote':
return [RemoteRuntime] return [RemoteRuntime]
elif runtime.lower() == 'runloop':
return [RunloopRuntime]
elif runtime.lower() == 'daytona':
return [DaytonaRuntime]
elif runtime.lower() == 'cli': elif runtime.lower() == 'cli':
return [CLIRuntime] return [CLIRuntime]
else: else:

View File

@ -990,34 +990,15 @@ def test_api_keys_repr_str():
app_config = OpenHandsConfig( app_config = OpenHandsConfig(
llms={'llm': llm_config}, llms={'llm': llm_config},
agents={'agent': agent_config}, agents={'agent': agent_config},
e2b_api_key='my_e2b_api_key', search_api_key='my_search_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',
) )
assert 'my_e2b_api_key' not in repr(app_config)
assert 'my_e2b_api_key' not in str(app_config) assert 'my_search_api_key' not in repr(app_config)
assert 'my_jwt_secret' not in repr(app_config) assert 'my_search_api_key' not in str(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)
# Check that no other attrs in OpenHandsConfig have 'key' or 'token' in their name # 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 # This will fail when new attrs are added, and attract attention
known_key_token_attrs_app = [ 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', 'search_api_key',
] ]
for attr_name in OpenHandsConfig.model_fields.keys(): for attr_name in OpenHandsConfig.model_fields.keys():

View File

@ -84,11 +84,11 @@ def test_llm_config_attributes_masking(test_handler):
def test_app_config_attributes_masking(test_handler): def test_app_config_attributes_masking(test_handler):
logger, stream = 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}') logger.info(f'App Config: {app_config}')
log_output = stream.getvalue() log_output = stream.getvalue()
assert 'github_token' not in log_output 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 assert 'ghp_abcdefghijklmnopqrstuvwxyz' not in log_output

14
third_party/__init__.py vendored Normal file
View 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
View File

@ -0,0 +1 @@
"""Third-party runtime implementations."""

1
third_party/runtime/impl/__init__.py vendored Normal file
View File

@ -0,0 +1 @@
"""Third-party runtime implementation modules."""

View 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')
"""

View File

@ -1,3 +1,4 @@
import os
from typing import Callable from typing import Callable
import httpx import httpx
@ -45,7 +46,13 @@ class DaytonaRuntime(ActionExecutionClient):
user_id: str | None = None, user_id: str | None = None,
git_provider_tokens: PROVIDER_TOKEN_TYPE | 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.config = config
self.sid = sid self.sid = sid
@ -53,9 +60,9 @@ class DaytonaRuntime(ActionExecutionClient):
self._vscode_url: str | None = None self._vscode_url: str | None = None
daytona_config = DaytonaConfig( daytona_config = DaytonaConfig(
api_key=config.daytona_api_key.get_secret_value(), api_key=daytona_api_key,
server_url=config.daytona_api_url, server_url=daytona_api_url,
target=config.daytona_target, target=daytona_target,
) )
self.daytona = Daytona(daytona_config) self.daytona = Daytona(daytona_config)

View File

@ -0,0 +1,5 @@
"""E2B runtime implementation.
This runtime reads configuration directly from environment variables:
- E2B_API_KEY: API key for E2B authentication
"""

View File

@ -16,8 +16,8 @@ from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
from openhands.runtime.impl.action_execution.action_execution_client import ( from openhands.runtime.impl.action_execution.action_execution_client import (
ActionExecutionClient, ActionExecutionClient,
) )
from openhands.runtime.impl.e2b.filestore import E2BFileStore from third_party.runtime.impl.e2b.filestore import E2BFileStore
from openhands.runtime.impl.e2b.sandbox import E2BSandbox from third_party.runtime.impl.e2b.sandbox import E2BSandbox
from openhands.runtime.plugins import PluginRequirement from openhands.runtime.plugins import PluginRequirement
from openhands.runtime.utils.files import insert_lines, read_lines from openhands.runtime.utils.files import insert_lines, read_lines
@ -50,7 +50,7 @@ class E2BRuntime(ActionExecutionClient):
git_provider_tokens, git_provider_tokens,
) )
if sandbox is None: if sandbox is None:
self.sandbox = E2BSandbox() self.sandbox = E2BSandbox(config.sandbox)
if not isinstance(self.sandbox, E2BSandbox): if not isinstance(self.sandbox, E2BSandbox):
raise ValueError('E2BRuntime requires an E2BSandbox') raise ValueError('E2BRuntime requires an E2BSandbox')
self.file_store = E2BFileStore(self.sandbox.filesystem) self.file_store = E2BFileStore(self.sandbox.filesystem)

View File

@ -19,11 +19,16 @@ class E2BBox:
def __init__( def __init__(
self, self,
config: SandboxConfig, config: SandboxConfig,
e2b_api_key: str,
template: str = 'openhands', template: str = 'openhands',
): ):
self.config = copy.deepcopy(config) self.config = copy.deepcopy(config)
self.initialize_plugins: bool = config.initialize_plugins 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( self.sandbox = E2BSandbox(
api_key=e2b_api_key, api_key=e2b_api_key,
template=template, template=template,
@ -112,3 +117,7 @@ class E2BBox:
def get_working_directory(self): def get_working_directory(self):
return self.sandbox.cwd return self.sandbox.cwd
# Alias for backward compatibility
E2BSandbox = E2BBox

View 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
"""

View File

@ -57,16 +57,22 @@ class ModalRuntime(ActionExecutionClient):
user_id: str | None = None, user_id: str | None = None,
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None, git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
): ):
assert config.modal_api_token_id, 'Modal API token id is required' # Read Modal API credentials from environment variables
assert config.modal_api_token_secret, 'Modal API token secret is required' 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.config = config
self.sandbox = None self.sandbox = None
self.sid = sid self.sid = sid
self.modal_client = modal.Client.from_credentials( self.modal_client = modal.Client.from_credentials(
config.modal_api_token_id.get_secret_value(), modal_token_id,
config.modal_api_token_secret.get_secret_value(), modal_token_secret,
) )
self.app = modal.App.lookup( self.app = modal.App.lookup(
'openhands', create_if_missing=True, client=self.modal_client 'openhands', create_if_missing=True, client=self.modal_client

View File

@ -0,0 +1,5 @@
"""Runloop runtime implementation.
This runtime reads configuration directly from environment variables:
- RUNLOOP_API_KEY: API key for Runloop authentication
"""

View File

@ -1,4 +1,5 @@
import logging import logging
import os
from typing import Callable from typing import Callable
import tenacity import tenacity
@ -40,11 +41,15 @@ class RunloopRuntime(ActionExecutionClient):
user_id: str | None = None, user_id: str | None = None,
git_provider_tokens: PROVIDER_TOKEN_TYPE | 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.devbox: DevboxView | None = None
self.config = config self.config = config
self.runloop_api_client = Runloop( 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 self.container_name = CONTAINER_NAME_PREFIX + sid
super().__init__( super().__init__(