modal 1.0.6.dev58__py3-none-any.whl → 1.2.3.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- modal/__main__.py +3 -4
- modal/_billing.py +80 -0
- modal/_clustered_functions.py +7 -3
- modal/_clustered_functions.pyi +4 -2
- modal/_container_entrypoint.py +41 -49
- modal/_functions.py +424 -195
- modal/_grpc_client.py +171 -0
- modal/_load_context.py +105 -0
- modal/_object.py +68 -20
- modal/_output.py +58 -45
- modal/_partial_function.py +36 -11
- modal/_pty.py +7 -3
- modal/_resolver.py +21 -35
- modal/_runtime/asgi.py +4 -3
- modal/_runtime/container_io_manager.py +301 -186
- modal/_runtime/container_io_manager.pyi +70 -61
- modal/_runtime/execution_context.py +18 -2
- modal/_runtime/execution_context.pyi +4 -1
- modal/_runtime/gpu_memory_snapshot.py +170 -63
- modal/_runtime/user_code_imports.py +28 -58
- modal/_serialization.py +57 -1
- modal/_utils/async_utils.py +33 -12
- modal/_utils/auth_token_manager.py +2 -5
- modal/_utils/blob_utils.py +110 -53
- modal/_utils/function_utils.py +49 -42
- modal/_utils/grpc_utils.py +80 -50
- modal/_utils/mount_utils.py +26 -1
- modal/_utils/name_utils.py +17 -3
- modal/_utils/task_command_router_client.py +536 -0
- modal/_utils/time_utils.py +34 -6
- modal/app.py +219 -83
- modal/app.pyi +229 -56
- modal/billing.py +5 -0
- modal/{requirements → builder}/2025.06.txt +1 -0
- modal/{requirements → builder}/PREVIEW.txt +1 -0
- modal/cli/_download.py +19 -3
- modal/cli/_traceback.py +3 -2
- modal/cli/app.py +4 -4
- modal/cli/cluster.py +15 -7
- modal/cli/config.py +5 -3
- modal/cli/container.py +7 -6
- modal/cli/dict.py +22 -16
- modal/cli/entry_point.py +12 -5
- modal/cli/environment.py +5 -4
- modal/cli/import_refs.py +3 -3
- modal/cli/launch.py +102 -5
- modal/cli/network_file_system.py +9 -13
- modal/cli/profile.py +3 -2
- modal/cli/programs/launch_instance_ssh.py +94 -0
- modal/cli/programs/run_jupyter.py +1 -1
- modal/cli/programs/run_marimo.py +95 -0
- modal/cli/programs/vscode.py +1 -1
- modal/cli/queues.py +57 -26
- modal/cli/run.py +58 -16
- modal/cli/secret.py +48 -22
- modal/cli/utils.py +3 -4
- modal/cli/volume.py +28 -25
- modal/client.py +13 -116
- modal/client.pyi +9 -91
- modal/cloud_bucket_mount.py +5 -3
- modal/cloud_bucket_mount.pyi +5 -1
- modal/cls.py +130 -102
- modal/cls.pyi +45 -85
- modal/config.py +29 -10
- modal/container_process.py +291 -13
- modal/container_process.pyi +95 -32
- modal/dict.py +282 -63
- modal/dict.pyi +423 -73
- modal/environments.py +15 -27
- modal/environments.pyi +5 -15
- modal/exception.py +8 -0
- modal/experimental/__init__.py +143 -38
- modal/experimental/flash.py +247 -78
- modal/experimental/flash.pyi +137 -9
- modal/file_io.py +14 -28
- modal/file_io.pyi +2 -2
- modal/file_pattern_matcher.py +25 -16
- modal/functions.pyi +134 -61
- modal/image.py +255 -86
- modal/image.pyi +300 -62
- modal/io_streams.py +436 -126
- modal/io_streams.pyi +236 -171
- modal/mount.py +62 -157
- modal/mount.pyi +45 -172
- modal/network_file_system.py +30 -53
- modal/network_file_system.pyi +16 -76
- modal/object.pyi +42 -8
- modal/parallel_map.py +821 -113
- modal/parallel_map.pyi +134 -0
- modal/partial_function.pyi +4 -1
- modal/proxy.py +16 -7
- modal/proxy.pyi +10 -2
- modal/queue.py +263 -61
- modal/queue.pyi +409 -66
- modal/runner.py +112 -92
- modal/runner.pyi +45 -27
- modal/sandbox.py +451 -124
- modal/sandbox.pyi +513 -67
- modal/secret.py +291 -67
- modal/secret.pyi +425 -19
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +11 -8
- modal/token_flow.py +4 -4
- modal/volume.py +344 -98
- modal/volume.pyi +464 -68
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
- modal-1.2.3.dev7.dist-info/RECORD +195 -0
- modal_docs/mdmd/mdmd.py +11 -1
- modal_proto/api.proto +399 -67
- modal_proto/api_grpc.py +241 -1
- modal_proto/api_pb2.py +1395 -1000
- modal_proto/api_pb2.pyi +1239 -79
- modal_proto/api_pb2_grpc.py +499 -4
- modal_proto/api_pb2_grpc.pyi +162 -14
- modal_proto/modal_api_grpc.py +175 -160
- modal_proto/sandbox_router.proto +145 -0
- modal_proto/sandbox_router_grpc.py +105 -0
- modal_proto/sandbox_router_pb2.py +149 -0
- modal_proto/sandbox_router_pb2.pyi +333 -0
- modal_proto/sandbox_router_pb2_grpc.py +203 -0
- modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
- modal_proto/task_command_router.proto +144 -0
- modal_proto/task_command_router_grpc.py +105 -0
- modal_proto/task_command_router_pb2.py +149 -0
- modal_proto/task_command_router_pb2.pyi +333 -0
- modal_proto/task_command_router_pb2_grpc.py +203 -0
- modal_proto/task_command_router_pb2_grpc.pyi +75 -0
- modal_version/__init__.py +1 -1
- modal-1.0.6.dev58.dist-info/RECORD +0 -183
- modal_proto/modal_options_grpc.py +0 -3
- modal_proto/options.proto +0 -19
- modal_proto/options_grpc.py +0 -3
- modal_proto/options_pb2.py +0 -35
- modal_proto/options_pb2.pyi +0 -20
- modal_proto/options_pb2_grpc.py +0 -4
- modal_proto/options_pb2_grpc.pyi +0 -7
- /modal/{requirements → builder}/2023.12.312.txt +0 -0
- /modal/{requirements → builder}/2023.12.txt +0 -0
- /modal/{requirements → builder}/2024.04.txt +0 -0
- /modal/{requirements → builder}/2024.10.txt +0 -0
- /modal/{requirements → builder}/README.md +0 -0
- /modal/{requirements → builder}/base-images.json +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/environments.pyi
CHANGED
|
@@ -45,10 +45,8 @@ class _Environment(modal._object._Object):
|
|
|
45
45
|
|
|
46
46
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
|
47
47
|
@staticmethod
|
|
48
|
-
def from_name(
|
|
49
|
-
|
|
50
|
-
async def lookup(
|
|
51
|
-
name: str, client: typing.Optional[modal.client._Client] = None, create_if_missing: bool = False
|
|
48
|
+
def from_name(
|
|
49
|
+
name: str, *, create_if_missing: bool = False, client: typing.Optional[modal.client._Client] = None
|
|
52
50
|
): ...
|
|
53
51
|
|
|
54
52
|
class Environment(modal.object.Object):
|
|
@@ -60,17 +58,9 @@ class Environment(modal.object.Object):
|
|
|
60
58
|
|
|
61
59
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
|
62
60
|
@staticmethod
|
|
63
|
-
def from_name(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def __call__(
|
|
67
|
-
self, /, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
|
|
68
|
-
): ...
|
|
69
|
-
async def aio(
|
|
70
|
-
self, /, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
|
|
71
|
-
): ...
|
|
72
|
-
|
|
73
|
-
lookup: __lookup_spec
|
|
61
|
+
def from_name(
|
|
62
|
+
name: str, *, create_if_missing: bool = False, client: typing.Optional[modal.client.Client] = None
|
|
63
|
+
): ...
|
|
74
64
|
|
|
75
65
|
async def _get_environment_cached(name: str, client: modal.client._Client) -> _Environment: ...
|
|
76
66
|
|
modal/exception.py
CHANGED
|
@@ -26,6 +26,10 @@ class Error(Exception):
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
class AlreadyExistsError(Error):
|
|
30
|
+
"""Raised when a resource creation conflicts with an existing resource."""
|
|
31
|
+
|
|
32
|
+
|
|
29
33
|
class RemoteError(Error):
|
|
30
34
|
"""Raised when an error occurs on the Modal server."""
|
|
31
35
|
|
|
@@ -38,6 +42,10 @@ class SandboxTimeoutError(TimeoutError):
|
|
|
38
42
|
"""Raised when a Sandbox exceeds its execution duration limit and times out."""
|
|
39
43
|
|
|
40
44
|
|
|
45
|
+
class ExecTimeoutError(TimeoutError):
|
|
46
|
+
"""Raised when a container process exceeds its execution duration limit and times out."""
|
|
47
|
+
|
|
48
|
+
|
|
41
49
|
class SandboxTerminatedError(Error):
|
|
42
50
|
"""Raised when a Sandbox is terminated for an internal reason."""
|
|
43
51
|
|
modal/experimental/__init__.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright Modal Labs 2025
|
|
2
2
|
import os
|
|
3
|
+
import shlex
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Literal, Optional, Union
|
|
@@ -12,15 +13,13 @@ from .._object import _get_environment_name
|
|
|
12
13
|
from .._partial_function import _clustered
|
|
13
14
|
from .._runtime.container_io_manager import _ContainerIOManager
|
|
14
15
|
from .._utils.async_utils import synchronize_api, synchronizer
|
|
15
|
-
from .._utils.deprecation import deprecation_warning
|
|
16
|
-
from .._utils.grpc_utils import retry_transient_errors
|
|
17
16
|
from ..app import _App
|
|
18
17
|
from ..client import _Client
|
|
19
|
-
from ..cls import _Cls
|
|
18
|
+
from ..cls import _Cls
|
|
20
19
|
from ..exception import InvalidError
|
|
21
20
|
from ..image import DockerfileSpec, ImageBuilderVersion, _Image, _ImageRegistryConfig
|
|
22
21
|
from ..secret import _Secret
|
|
23
|
-
from .flash import flash_forward, flash_prometheus_autoscaler # noqa: F401
|
|
22
|
+
from .flash import flash_forward, flash_get_containers, flash_prometheus_autoscaler # noqa: F401
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
def stop_fetching_inputs():
|
|
@@ -116,7 +115,7 @@ async def get_app_objects(
|
|
|
116
115
|
|
|
117
116
|
app = await _App.lookup(app_name, environment_name=environment_name, client=client)
|
|
118
117
|
req = api_pb2.AppGetLayoutRequest(app_id=app.app_id)
|
|
119
|
-
app_layout_resp = await
|
|
118
|
+
app_layout_resp = await client.stub.AppGetLayout(req)
|
|
120
119
|
|
|
121
120
|
app_objects: dict[str, Union[_Function, _Cls]] = {}
|
|
122
121
|
|
|
@@ -212,47 +211,153 @@ async def raw_registry_image(
|
|
|
212
211
|
)
|
|
213
212
|
|
|
214
213
|
|
|
214
|
+
def _install_cuda_command() -> str:
|
|
215
|
+
"""Command to install CUDA Toolkit (nvcc) inside a container."""
|
|
216
|
+
arch = "x86_64" # instruction set architecture for the CPU, all Modal machines are x86_64
|
|
217
|
+
distro = "debian12" # the distribution and version number of our OS (GNU/Linux)
|
|
218
|
+
filename = "cuda-keyring_1.1-1_all.deb" # NVIDIA signing key file
|
|
219
|
+
cuda_keyring_url = f"https://developer.download.nvidia.com/compute/cuda/repos/{distro}/{arch}/{filename}"
|
|
220
|
+
|
|
221
|
+
major, minor = 12, 8
|
|
222
|
+
max_cuda_version = f"{major}-{minor}"
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
f"wget {cuda_keyring_url} && "
|
|
226
|
+
+ f"dpkg -i {filename} && "
|
|
227
|
+
+ f"rm -f {filename} && "
|
|
228
|
+
+ f"apt-get update && apt-get install -y cuda-nvcc-{max_cuda_version}"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@synchronizer.create_blocking
|
|
233
|
+
async def notebook_base_image(*, python_version: Optional[str] = None, force_build: bool = False) -> _Image:
|
|
234
|
+
"""Default image used for Modal notebook kernels, with common libraries.
|
|
235
|
+
|
|
236
|
+
This can be used to bootstrap development workflows quickly. We don't
|
|
237
|
+
recommend using this image for production Modal Functions though, as it may
|
|
238
|
+
change at any time in the future.
|
|
239
|
+
"""
|
|
240
|
+
# Include several common packages, as well as kernelshim dependencies (except 'modal').
|
|
241
|
+
# These packages aren't pinned, so they may change over time with builds.
|
|
242
|
+
#
|
|
243
|
+
# We plan to use `--exclude-newer` in the future, with date-specific image builds.
|
|
244
|
+
base_image = _Image.debian_slim(python_version=python_version)
|
|
245
|
+
|
|
246
|
+
environment_packages: list[str] = [
|
|
247
|
+
"accelerate",
|
|
248
|
+
"aiohttp",
|
|
249
|
+
"altair",
|
|
250
|
+
"anthropic",
|
|
251
|
+
"asyncpg",
|
|
252
|
+
"beautifulsoup4",
|
|
253
|
+
"bokeh",
|
|
254
|
+
"boto3[crt]",
|
|
255
|
+
"click",
|
|
256
|
+
"diffusers[torch,flax]",
|
|
257
|
+
"dm-sonnet",
|
|
258
|
+
"flax",
|
|
259
|
+
"ftfy",
|
|
260
|
+
"h5py",
|
|
261
|
+
"urllib3",
|
|
262
|
+
"httpx",
|
|
263
|
+
"huggingface-hub",
|
|
264
|
+
"ipywidgets",
|
|
265
|
+
"jax[cuda12]",
|
|
266
|
+
"keras",
|
|
267
|
+
"matplotlib",
|
|
268
|
+
"nbformat",
|
|
269
|
+
"numba",
|
|
270
|
+
"numpy",
|
|
271
|
+
"openai",
|
|
272
|
+
"optax",
|
|
273
|
+
"pandas",
|
|
274
|
+
"plotly[express]",
|
|
275
|
+
"polars",
|
|
276
|
+
"psycopg2",
|
|
277
|
+
"requests",
|
|
278
|
+
"safetensors",
|
|
279
|
+
"scikit-image",
|
|
280
|
+
"scikit-learn",
|
|
281
|
+
"scipy",
|
|
282
|
+
"seaborn",
|
|
283
|
+
"sentencepiece",
|
|
284
|
+
"sqlalchemy",
|
|
285
|
+
"statsmodels",
|
|
286
|
+
"sympy",
|
|
287
|
+
"tabulate",
|
|
288
|
+
"tensorboard",
|
|
289
|
+
"toml",
|
|
290
|
+
"transformers",
|
|
291
|
+
"triton",
|
|
292
|
+
"typer",
|
|
293
|
+
"vega-datasets",
|
|
294
|
+
"watchfiles",
|
|
295
|
+
"websockets",
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
# Kernelshim dependencies. (see NOTEBOOK_KERNELSHIM_DEPENDENCIES)
|
|
299
|
+
kernelshim_packages: list[str] = [
|
|
300
|
+
"authlib>=1.3",
|
|
301
|
+
"basedpyright>=1.28",
|
|
302
|
+
"fastapi>=0.100",
|
|
303
|
+
"ipykernel>=6",
|
|
304
|
+
"pydantic>=2",
|
|
305
|
+
"pyzmq>=26",
|
|
306
|
+
"ruff>=0.11",
|
|
307
|
+
"uvicorn>=0.32",
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
commands: list[str] = [
|
|
311
|
+
"apt-get update",
|
|
312
|
+
"apt-get install -y "
|
|
313
|
+
+ "libpq-dev pkg-config cmake git curl wget unzip zip libsqlite3-dev openssh-server vim ffmpeg",
|
|
314
|
+
_install_cuda_command(),
|
|
315
|
+
# Install uv since it's faster than pip for installing packages.
|
|
316
|
+
"pip install uv",
|
|
317
|
+
# https://github.com/astral-sh/uv/issues/11480
|
|
318
|
+
"pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu129",
|
|
319
|
+
f"uv pip install --system {shlex.join(sorted(environment_packages))}",
|
|
320
|
+
f"uv pip install --system {shlex.join(sorted(kernelshim_packages))}",
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
|
324
|
+
return DockerfileSpec(
|
|
325
|
+
commands=[
|
|
326
|
+
"FROM base",
|
|
327
|
+
*(f"RUN {cmd}" for cmd in commands),
|
|
328
|
+
"ENV PATH=/usr/local/cuda/bin:$PATH",
|
|
329
|
+
],
|
|
330
|
+
context_files={},
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
return _Image._from_args(
|
|
334
|
+
base_images={"base": base_image},
|
|
335
|
+
dockerfile_function=build_dockerfile,
|
|
336
|
+
force_build=force_build,
|
|
337
|
+
_namespace=api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|
|
215
341
|
@synchronizer.create_blocking
|
|
216
|
-
async def
|
|
217
|
-
|
|
342
|
+
async def image_delete(
|
|
343
|
+
image_id: str,
|
|
218
344
|
*,
|
|
219
|
-
min_containers: Optional[int] = None,
|
|
220
|
-
max_containers: Optional[int] = None,
|
|
221
|
-
buffer_containers: Optional[int] = None,
|
|
222
|
-
scaledown_window: Optional[int] = None,
|
|
223
345
|
client: Optional[_Client] = None,
|
|
224
346
|
) -> None:
|
|
225
|
-
"""
|
|
347
|
+
"""Delete an Image by its ID.
|
|
348
|
+
|
|
349
|
+
Deletion is irreversible and will prevent Apps from using the Image.
|
|
226
350
|
|
|
227
351
|
This is an experimental interface for a feature that we will be adding to
|
|
228
|
-
|
|
229
|
-
may look different (i.e., it may be a standalone function or a method).
|
|
352
|
+
the main Image class. The stable form of this interface may look different.
|
|
230
353
|
|
|
354
|
+
Note: When building an Image, each chained method call will create an
|
|
355
|
+
intermediate Image layer, each with its own ID. Deleting an Image will not
|
|
356
|
+
delete any of its intermediate layers, only the image identified by the
|
|
357
|
+
provided ID.
|
|
231
358
|
"""
|
|
232
|
-
deprecation_warning(
|
|
233
|
-
(2025, 5, 5),
|
|
234
|
-
"The modal.experimental.update_autoscaler(...) function is now deprecated in favor of"
|
|
235
|
-
" a stable `.update_autoscaler(...) method on the corresponding object.",
|
|
236
|
-
show_source=True,
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
settings = api_pb2.AutoscalerSettings(
|
|
240
|
-
min_containers=min_containers,
|
|
241
|
-
max_containers=max_containers,
|
|
242
|
-
buffer_containers=buffer_containers,
|
|
243
|
-
scaledown_window=scaledown_window,
|
|
244
|
-
)
|
|
245
|
-
|
|
246
359
|
if client is None:
|
|
247
360
|
client = await _Client.from_env()
|
|
248
361
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
else:
|
|
252
|
-
assert obj._cls._class_service_function is not None
|
|
253
|
-
await obj._cls._class_service_function.hydrate(client=client)
|
|
254
|
-
f = obj._cached_service_function()
|
|
255
|
-
await f.hydrate(client=client)
|
|
256
|
-
|
|
257
|
-
request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=f.object_id, settings=settings)
|
|
258
|
-
await retry_transient_errors(client.stub.FunctionUpdateSchedulingParams, request)
|
|
362
|
+
req = api_pb2.ImageDeleteRequest(image_id=image_id)
|
|
363
|
+
await client.stub.ImageDelete(req)
|