fix: daytona runtime sandbox handling (#9187)

Signed-off-by: Ivan Dagelic <dagelic.ivan@gmail.com>
This commit is contained in:
Ivan Dagelic 2025-06-17 22:18:46 +02:00 committed by GitHub
parent 30c71776e7
commit bae6bd77f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 169 additions and 112 deletions

View File

@ -1,14 +1,13 @@
import json
from typing import Callable
import httpx
import tenacity
from daytona_sdk import (
CreateWorkspaceParams,
from daytona import (
CreateSandboxFromSnapshotParams,
Daytona,
DaytonaConfig,
Sandbox,
SessionExecuteRequest,
Workspace,
)
from openhands.core.config.openhands_config import OpenHandsConfig
@ -24,11 +23,11 @@ from openhands.runtime.utils.request import RequestHTTPError
from openhands.utils.async_utils import call_sync_from_async
from openhands.utils.tenacity_stop import stop_if_should_exit
WORKSPACE_PREFIX = 'openhands-sandbox-'
OPENHANDS_SID_LABEL = 'OpenHands_SID'
class DaytonaRuntime(ActionExecutionClient):
"""The DaytonaRuntime class is a DockerRuntime that utilizes Daytona workspace as a runtime environment."""
"""The DaytonaRuntime class is a DockerRuntime that utilizes Daytona Sandboxes as runtime environments."""
_sandbox_port: int = 4444
_vscode_port: int = 4445
@ -50,8 +49,7 @@ class DaytonaRuntime(ActionExecutionClient):
self.config = config
self.sid = sid
self.workspace_id = WORKSPACE_PREFIX + sid
self.workspace: Workspace | None = None
self.sandbox: Sandbox | None = None
self._vscode_url: str | None = None
daytona_config = DaytonaConfig(
@ -81,20 +79,24 @@ class DaytonaRuntime(ActionExecutionClient):
git_provider_tokens,
)
def _get_workspace(self) -> Workspace | None:
def _get_sandbox(self) -> Sandbox | None:
try:
workspace = self.daytona.get_current_workspace(self.workspace_id)
self.log(
'info', f'Attached to existing workspace with id: {self.workspace_id}'
)
sandboxes = self.daytona.list({OPENHANDS_SID_LABEL: self.sid})
if len(sandboxes) == 0:
return None
assert len(sandboxes) == 1, 'Multiple sandboxes found for SID'
sandbox = sandboxes[0]
self.log('info', f'Attached to existing sandbox with id: {self.sid}')
except Exception:
self.log(
'warning',
f'Failed to attach to existing workspace with id: {self.workspace_id}',
f'Failed to attach to existing sandbox with id: {self.sid}',
)
workspace = None
sandbox = None
return workspace
return sandbox
def _get_creation_env_vars(self) -> dict[str, str]:
env_vars: dict[str, str] = {
@ -108,37 +110,28 @@ class DaytonaRuntime(ActionExecutionClient):
return env_vars
def _create_workspace(self) -> Workspace:
workspace_params = CreateWorkspaceParams(
id=self.workspace_id,
def _create_sandbox(self) -> Sandbox:
sandbox_params = CreateSandboxFromSnapshotParams(
language='python',
image=self.config.sandbox.runtime_container_image,
snapshot=self.config.sandbox.runtime_container_image,
public=True,
env_vars=self._get_creation_env_vars(),
labels={OPENHANDS_SID_LABEL: self.sid},
)
workspace = self.daytona.create(workspace_params)
return workspace
return self.daytona.create(sandbox_params)
def _construct_api_url(self, port: int) -> str:
assert self.workspace is not None, 'Workspace is not initialized'
assert self.workspace.instance.info is not None, (
'Workspace info is not available'
)
assert self.workspace.instance.info.provider_metadata is not None, (
'Provider metadata is not available'
)
assert self.sandbox is not None, 'Sandbox is not initialized'
assert self.sandbox.runner_domain is not None, 'Runner domain is not available'
node_domain = json.loads(self.workspace.instance.info.provider_metadata)[
'nodeDomain'
]
return f'https://{port}-{self.workspace.id}.{node_domain}'
return f'https://{port}-{self.sandbox.id}.{self.sandbox.runner_domain}'
@property
def action_execution_server_url(self) -> str:
return self.api_url
def _start_action_execution_server(self) -> None:
assert self.workspace is not None, 'Workspace is not initialized'
assert self.sandbox is not None, 'Sandbox is not initialized'
start_command: list[str] = get_action_execution_server_startup_command(
server_port=self._sandbox_port,
@ -158,9 +151,9 @@ class DaytonaRuntime(ActionExecutionClient):
)
exec_session_id = 'action-execution-server'
self.workspace.process.create_session(exec_session_id)
self.sandbox.process.create_session(exec_session_id)
exec_command = self.workspace.process.execute_session_command(
exec_command = self.sandbox.process.execute_session_command(
exec_session_id,
SessionExecuteRequest(command=start_command_str, var_async=True),
)
@ -180,27 +173,27 @@ class DaytonaRuntime(ActionExecutionClient):
should_start_action_execution_server = False
if self.attach_to_existing:
self.workspace = await call_sync_from_async(self._get_workspace)
self.sandbox = await call_sync_from_async(self._get_sandbox)
else:
should_start_action_execution_server = True
if self.workspace is None:
if self.sandbox is None:
self.set_runtime_status(RuntimeStatus.BUILDING_RUNTIME)
self.workspace = await call_sync_from_async(self._create_workspace)
self.log('info', f'Created new workspace with id: {self.workspace_id}')
self.sandbox = await call_sync_from_async(self._create_sandbox)
self.log('info', f'Created a new sandbox with id: {self.sid}')
self.api_url = self._construct_api_url(self._sandbox_port)
state = self.workspace.instance.state
state = self.sandbox.state
if state == 'stopping':
self.log('info', 'Waiting for Daytona workspace to stop...')
await call_sync_from_async(self.workspace.wait_for_workspace_stop)
self.log('info', 'Waiting for the Daytona sandbox to stop...')
await call_sync_from_async(self.sandbox.wait_for_sandbox_stop)
state = 'stopped'
if state == 'stopped':
self.log('info', 'Starting Daytona workspace...')
await call_sync_from_async(self.workspace.start)
self.log('info', 'Starting the Daytona sandbox...')
await call_sync_from_async(self.sandbox.start)
should_start_action_execution_server = True
if should_start_action_execution_server:
@ -247,8 +240,8 @@ class DaytonaRuntime(ActionExecutionClient):
if self.attach_to_existing:
return
if self.workspace:
self.daytona.remove(self.workspace)
if self.sandbox:
self.sandbox.delete()
@property
def vscode_url(self) -> str | None:
@ -260,9 +253,9 @@ class DaytonaRuntime(ActionExecutionClient):
'warning', 'Failed to get VSCode token while trying to get VSCode URL'
)
return None
if not self.workspace:
if not self.sandbox:
self.log(
'warning', 'Workspace is not initialized while trying to get VSCode URL'
'warning', 'Sandbox is not initialized while trying to get VSCode URL'
)
return None
self._vscode_url = (

188
poetry.lock generated
View File

@ -1,4 +1,50 @@
# 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
python-versions = "<4.0,>=3.8"
groups = ["main"]
files = [
{file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"},
{file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"},
]
[package.dependencies]
aiobotocore = {version = "2.22.0", extras = ["boto3"]}
aiofiles = ">=23.2.1"
[package.extras]
chalice = ["chalice (>=1.24.0)"]
s3cse = ["cryptography (>=44.0.1)"]
[[package]]
name = "aiobotocore"
version = "2.22.0"
description = "Async client for aws services using botocore and aiohttp"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"},
{file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"},
]
[package.dependencies]
aiohttp = ">=3.9.2,<4.0.0"
aioitertools = ">=0.5.1,<1.0.0"
boto3 = {version = ">=1.37.2,<1.37.4", optional = true, markers = "extra == \"boto3\""}
botocore = ">=1.37.2,<1.37.4"
jmespath = ">=0.7.1,<2.0.0"
multidict = ">=6.0.0,<7.0.0"
python-dateutil = ">=2.1,<3.0.0"
wrapt = ">=1.10.10,<2.0.0"
[package.extras]
awscli = ["awscli (>=1.38.2,<1.38.4)"]
boto3 = ["boto3 (>=1.37.2,<1.37.4)"]
[[package]]
name = "aiofiles"
@ -147,6 +193,22 @@ files = [
[package.dependencies]
aiohttp = "*"
[[package]]
name = "aioitertools"
version = "0.12.0"
description = "itertools and builtins for AsyncIO and mixed iterables"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"},
{file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"},
]
[package.extras]
dev = ["attribution (==1.8.0)", "black (==24.8.0)", "build (>=1.2)", "coverage (==7.6.1)", "flake8 (==7.1.1)", "flit (==3.9.0)", "mypy (==1.11.2)", "ufmt (==2.7.1)", "usort (==1.0.8.post1)"]
docs = ["sphinx (==8.0.2)", "sphinx-mdinclude (==0.6.2)"]
[[package]]
name = "aiolimiter"
version = "1.2.1"
@ -400,7 +462,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"},
@ -581,20 +643,20 @@ files = [
[[package]]
name = "boto3"
version = "1.38.36"
version = "1.37.3"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "boto3-1.38.36-py3-none-any.whl", hash = "sha256:34c27d7317cadb62c0e9856e5d5aa0271ef47202d340584831048bc7ac904136"},
{file = "boto3-1.38.36.tar.gz", hash = "sha256:efe0aaa060f8fedd76e5c942055f051aee0432fc722d79d8830a9fd9db83593e"},
{file = "boto3-1.37.3-py3-none-any.whl", hash = "sha256:2063b40af99fd02f6228ff52397b552ff3353831edaf8d25cc04801827ab9794"},
{file = "boto3-1.37.3.tar.gz", hash = "sha256:21f3ce0ef111297e63a6eb998a25197b8c10982970c320d4c6e8db08be2157be"},
]
[package.dependencies]
botocore = ">=1.38.36,<1.39.0"
botocore = ">=1.37.3,<1.38.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.13.0,<0.14.0"
s3transfer = ">=0.11.0,<0.12.0"
[package.extras]
crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
@ -1028,14 +1090,14 @@ xray = ["mypy-boto3-xray (>=1.38.0,<1.39.0)"]
[[package]]
name = "botocore"
version = "1.38.36"
version = "1.37.3"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "botocore-1.38.36-py3-none-any.whl", hash = "sha256:b6a50b853f6d23af9edfed89a59800c6bc1687a947cdd3492879f7d64e002d30"},
{file = "botocore-1.38.36.tar.gz", hash = "sha256:4a1ced1a4218bdff0ed5b46abb54570d473154ddefafa5d121a8d96e4b76ebc1"},
{file = "botocore-1.37.3-py3-none-any.whl", hash = "sha256:d01bd3bf4c80e61fa88d636ad9f5c9f60a551d71549b481386c6b4efe0bb2b2e"},
{file = "botocore-1.37.3.tar.gz", hash = "sha256:fe8403eb55a88faf9b0f9da6615e5bee7be056d75e17af66c3c8f0a3b0648da4"},
]
[package.dependencies]
@ -1580,7 +1642,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"
@ -1932,16 +1994,48 @@ tests-numpy2 = ["Pillow (>=9.4.0)", "absl-py", "decorator", "elasticsearch (<8.0
torch = ["torch"]
vision = ["Pillow (>=9.4.0)"]
[[package]]
name = "daytona"
version = "0.21.1"
description = "Python SDK for Daytona"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "daytona-0.21.1-py3-none-any.whl", hash = "sha256:1ce6b352f52ef92e667098b7bdaa60c22ffbfb8e686a8cbd12418bf7698ac834"},
{file = "daytona-0.21.1.tar.gz", hash = "sha256:01d83dd2b627f87e82491fb97f41845768d75c33f0767eaa44f6e8378bd58e60"},
]
[package.dependencies]
aioboto3 = ">=14.0.0,<15.0.0"
aiofiles = ">=24.1.0,<24.2.0"
aiohttp = ">=3.12.0,<4.0.0"
aiohttp_retry = ">=2.9.0,<3.0.0"
boto3 = ">=1.0.0,<2.0.0"
daytona_api_client = ">=0.21.0,<0.22.0"
daytona_api_client_async = ">=0.21.0,<0.22.0"
Deprecated = ">=1.2.18,<2.0.0"
environs = ">=9.5.0,<10.0.0"
httpx = ">=0.28.0,<0.29.0"
marshmallow = ">=3.19.0,<4.0.0"
pydantic = ">=2.4.2,<3.0.0"
python-dateutil = ">=2.8.2,<3.0.0"
toml = ">=0.10.0,<0.11.0"
urllib3 = ">=2.0.7,<3.0.0"
[package.extras]
dev = ["black[jupyter] (>=23.1.0,<24.0.0)", "build (>=1.0.3)", "isort (>=5.10.0,<6.0.0)", "matplotlib (>=3.10.0,<3.11.0)", "nbqa (>=1.9.1,<2.0.0)", "pydoc-markdown (>=4.8.2)", "pylint (>=3.3.4,<4.0.0)", "setuptools (>=68.0.0)", "twine (>=4.0.2)", "unasync (>=0.6.0,<0.7.0)", "wheel (>=0.41.2)"]
[[package]]
name = "daytona-api-client"
version = "0.20.1"
version = "0.21.0"
description = "Daytona"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "daytona_api_client-0.20.1-py3-none-any.whl", hash = "sha256:4d5023108013365eba76bd0bd4704f30dee54c13e2ac5b62e8c88bcd4af5db92"},
{file = "daytona_api_client-0.20.1.tar.gz", hash = "sha256:ff2061f7e7dc9c935a9087216600be277cb9cf6b8c1eecdfe333ef30d6b208fd"},
{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"},
]
[package.dependencies]
@ -1952,14 +2046,14 @@ urllib3 = ">=1.25.3,<3.0.0"
[[package]]
name = "daytona-api-client-async"
version = "0.20.1"
version = "0.21.0"
description = "Daytona"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "daytona_api_client_async-0.20.1-py3-none-any.whl", hash = "sha256:f24e06e3ab6e554214ed064f1b4c8723356c76c14c69de9a73a6cad60a386127"},
{file = "daytona_api_client_async-0.20.1.tar.gz", hash = "sha256:043045cb173b0b53416c19a9e276124a5c4fe14209f409a8572ef1975240e53f"},
{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"},
]
[package.dependencies]
@ -1970,35 +2064,6 @@ python-dateutil = ">=2.8.2"
typing-extensions = ">=4.7.1"
urllib3 = ">=1.25.3,<3.0.0"
[[package]]
name = "daytona-sdk"
version = "0.20.0"
description = "Python SDK for Daytona"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "daytona_sdk-0.20.0-py3-none-any.whl", hash = "sha256:7919acfff21c072a0ea826a3b250c0d9c5765e58c054d2bd5b91ea76f0df4709"},
{file = "daytona_sdk-0.20.0.tar.gz", hash = "sha256:b5c13b999fcce1e6460974dbbb0dd336d8ca1e96d6a25afe705f476fba4e6f11"},
]
[package.dependencies]
aiofiles = ">=24.1.0,<24.2.0"
aiohttp = ">=3.12.0,<4.0.0"
aiohttp_retry = ">=2.9.0,<3.0.0"
daytona_api_client = ">=0.20.0,<0.21.0"
daytona_api_client_async = ">=0.20.0,<0.21.0"
Deprecated = ">=1.2.18,<2.0.0"
environs = ">=9.5.0,<10.0.0"
httpx = ">=0.28.0,<0.29.0"
marshmallow = ">=3.19.0,<4.0.0"
pydantic = ">=2.4.2,<3.0.0"
python-dateutil = ">=2.8.2,<3.0.0"
urllib3 = ">=2.0.7,<3.0.0"
[package.extras]
dev = ["black[jupyter] (>=23.1.0,<24.0.0)", "build (>=1.0.3)", "isort (>=5.10.0,<6.0.0)", "matplotlib (>=3.10.0,<3.11.0)", "nbqa (>=1.9.1,<2.0.0)", "pydoc-markdown (>=4.8.2)", "pylint (>=3.3.4,<4.0.0)", "setuptools (>=68.0.0)", "twine (>=4.0.2)", "unasync (>=0.6.0,<0.7.0)", "wheel (>=0.41.2)"]
[[package]]
name = "debugpy"
version = "1.8.14"
@ -2974,8 +3039,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"
@ -2997,8 +3062,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"
@ -3216,8 +3281,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"
@ -6479,8 +6544,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"
@ -8967,21 +9032,21 @@ typing-extensions = ">=4.10,<5"
[[package]]
name = "s3transfer"
version = "0.13.0"
version = "0.11.3"
description = "An Amazon S3 Transfer Manager"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "s3transfer-0.13.0-py3-none-any.whl", hash = "sha256:0148ef34d6dd964d0d8cf4311b2b21c474693e57c2e069ec708ce043d2b527be"},
{file = "s3transfer-0.13.0.tar.gz", hash = "sha256:f5e6db74eb7776a37208001113ea7aa97695368242b364d73e91c981ac522177"},
{file = "s3transfer-0.11.3-py3-none-any.whl", hash = "sha256:ca855bdeb885174b5ffa95b9913622459d4ad8e331fc98eb01e6d5eb6a30655d"},
{file = "s3transfer-0.11.3.tar.gz", hash = "sha256:edae4977e3a122445660c7c114bba949f9d191bae3b34a096f18a1c8c354527a"},
]
[package.dependencies]
botocore = ">=1.37.4,<2.0a.0"
botocore = ">=1.36.0,<2.0a.0"
[package.extras]
crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"]
crt = ["botocore[crt] (>=1.36.0,<2.0a.0)"]
[[package]]
name = "sacrebleu"
@ -9243,7 +9308,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\""]
@ -9486,7 +9550,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"},
@ -9503,7 +9567,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"},
@ -11665,4 +11729,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.1"
python-versions = "^3.12,<3.14"
content-hash = "47df4fc76b97147ff31169028edafaf35c1f4e661c7ab74bad48cb0ceea06aba"
content-hash = "df8217d9808a5a1f5886e0328cbeb5032b20c28a677154888bd010f7bc945cb2"

View File

@ -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-sdk = "0.20.0"
daytona = "0.21.1"
stripe = ">=11.5,<13.0"
google-cloud-aiplatform = "*"
anthropic = { extras = [ "vertex" ], version = "*" }