modal 1.6.3.dev12__tar.gz → 1.6.3.dev13__tar.gz
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.
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/PKG-INFO +1 -1
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_image.py +37 -11
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/docker_utils.py +10 -1
- modal-1.6.3.dev13/modal/builder/PREVIEW.txt +17 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/base-images.json +3 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/endpoint.py +49 -7
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/profile.py +1 -1
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/workspace.py +1 -1
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/client.pyi +2 -2
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/image.pyi +24 -12
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/io_streams.py +26 -14
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/io_streams.pyi +2 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/mount.py +9 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/SOURCES.txt +1 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_grpc.py +16 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2.py +1205 -1175
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2.pyi +52 -1
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2_grpc.py +33 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2_grpc.pyi +10 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_version/__init__.py +1 -1
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/LICENSE +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/README.md +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/__main__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_billing.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_cluster.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_cluster.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_container_entrypoint.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_environments.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_function_variants.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_functions.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_grpc_client.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_ipython.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_load_context.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_location.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_logs.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_logs_manager.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_logs_manager.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_metrics.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_metrics.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_object.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_outbound_policy.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_outbound_policy.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/manager.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/pty.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/rich.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/status.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_partial_function.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_resolver.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_resources.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/asgi.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/execution_context.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/telemetry.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_serialization.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_server.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_server.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_supports_logs.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_traceback.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_tunnel.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_tunnel.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_type_manager.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/app_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/async_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/blob_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/browser_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/curl_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/deprecation.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/display_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/function_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/git_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/hash_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/http_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/idle_countdown.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/logger.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/mount_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/name_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/oauth.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/package_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/proxy_support.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/shell_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/time_utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/tblib.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/version.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_watcher.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_workspace.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/app.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/app.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/billing.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/billing.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2023.12.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2024.04.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2024.10.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2025.06.txt +0 -0
- /modal-1.6.3.dev12/modal/builder/PREVIEW.txt → /modal-1.6.3.dev13/modal/builder/2026.10.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/README.md +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/call_graph.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_download.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_help.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_logs.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_stats.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_traceback.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/app.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/billing.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/changelog.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/config.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/container.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/curl.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/dashboard.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/dict.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/entry_point.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/environment.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/function.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/image.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/import_refs.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/logo.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/network_file_system.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/queues.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/run.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/secret.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/selector.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/server.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/shell.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/skills.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/token.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/utils.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/volume.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/client.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cls.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cls.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cluster.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cluster.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/config.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/container_process.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/container_process.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/dict.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/dict.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/environments.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/environments.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/exception.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/_flash_container_entry.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash_server.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash_server.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/ipython.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/file_pattern_matcher.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/functions.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/functions.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/image.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/mount.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/network_file_system.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/network_file_system.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/object.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/object.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/output.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/parallel_map.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/parallel_map.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/partial_function.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/partial_function.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/proxy.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/proxy.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/py.typed +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/queue.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/queue.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/retries.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/runner.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/runner.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/running_app.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_common.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_filesystem.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_impl_persistent.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_impl_v1.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_impl_v2.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_sandbox.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_sidecar.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_snapshot.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_task_command_router_client.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/sandbox.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/sandbox.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/schedule.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/scheduler_placement.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/secret.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/secret.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/server.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/server.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/serving.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/serving.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/stream_type.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/token_flow.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/token_flow.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/types.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/volume.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/volume.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/workspace.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/workspace.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/requires.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/top_level.txt +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/types.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/reference_intro.md +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/__init__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/py.typed +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_version/__main__.py +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/pyproject.toml +0 -0
- {modal-1.6.3.dev12 → modal-1.6.3.dev13}/setup.cfg +0 -0
|
@@ -40,6 +40,7 @@ from ._utils.blob_utils import MAX_OBJECT_SIZE_BYTES
|
|
|
40
40
|
from ._utils.docker_utils import (
|
|
41
41
|
extract_copy_command_patterns,
|
|
42
42
|
find_dockerignore_file,
|
|
43
|
+
is_multi_stage_dockerfile,
|
|
43
44
|
splittable_from_line_index,
|
|
44
45
|
)
|
|
45
46
|
from ._utils.function_utils import FunctionSourceInfo, parse_gpu_config
|
|
@@ -70,8 +71,8 @@ if typing.TYPE_CHECKING:
|
|
|
70
71
|
import modal.client
|
|
71
72
|
|
|
72
73
|
# This is used for both type checking and runtime validation
|
|
73
|
-
ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]
|
|
74
|
-
LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "
|
|
74
|
+
ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"]
|
|
75
|
+
LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "2026.10"
|
|
75
76
|
|
|
76
77
|
# Note: we also define supported Python versions via logic at the top of the package __init__.py
|
|
77
78
|
# so that we fail fast / clearly in unsupported containers. Additionally, we enumerate the supported
|
|
@@ -79,6 +80,7 @@ LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "2025.06"
|
|
|
79
80
|
# Consider consolidating these multiple sources of truth?
|
|
80
81
|
SUPPORTED_PYTHON_SERIES: dict[ImageBuilderVersion, list[str]] = {
|
|
81
82
|
"PREVIEW": ["3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
|
|
83
|
+
"2026.10": ["3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
|
|
82
84
|
"2025.06": ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
|
|
83
85
|
"2024.10": ["3.10", "3.11", "3.12", "3.13"],
|
|
84
86
|
"2024.04": ["3.10", "3.11", "3.12"],
|
|
@@ -2523,28 +2525,33 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2523
2525
|
*,
|
|
2524
2526
|
force_build: bool = False,
|
|
2525
2527
|
context_dir: Path | str | None = None,
|
|
2526
|
-
|
|
2528
|
+
ignore: Sequence[str] | Callable[[Path], bool] = AUTO_DOCKERIGNORE,
|
|
2529
|
+
build_args: dict[str, str] = {},
|
|
2530
|
+
registry_secret: _Secret | None = None,
|
|
2527
2531
|
secrets: Collection[_Secret] | None = None,
|
|
2532
|
+
env: dict[str, str | None] | None = None,
|
|
2528
2533
|
gpu: str | None = None,
|
|
2529
2534
|
add_python: str | None = None,
|
|
2530
|
-
build_args: dict[str, str] = {},
|
|
2531
|
-
ignore: Sequence[str] | Callable[[Path], bool] = AUTO_DOCKERIGNORE,
|
|
2532
2535
|
) -> "_Image":
|
|
2533
2536
|
"""Build a Modal image from a local Dockerfile.
|
|
2534
2537
|
|
|
2535
2538
|
If your Dockerfile does not have Python installed, you can use the `add_python` parameter
|
|
2536
2539
|
to specify a version of Python to add to the image.
|
|
2537
2540
|
|
|
2541
|
+
A `registry_secret` can be provided to pull the base image from a private registry.
|
|
2542
|
+
Currently this is supported only for single-stage Dockerfiles.
|
|
2543
|
+
|
|
2538
2544
|
Args:
|
|
2539
2545
|
path: Path to the Dockerfile on the local machine.
|
|
2540
2546
|
force_build: If True, skip cached image builds.
|
|
2541
2547
|
context_dir: Build context directory for resolving relative COPY paths.
|
|
2548
|
+
ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
|
|
2549
|
+
build_args: Dockerfile ``ARG`` values forwarded to the build.
|
|
2550
|
+
registry_secret: Optional Secret with credentials for pulling the base image from a private registry.
|
|
2551
|
+
secrets: Secrets injected as environment variables in the build container.
|
|
2542
2552
|
env: Environment variables set in the build container.
|
|
2543
|
-
|
|
2544
|
-
gpu: GPU type to attach to the builder container.
|
|
2553
|
+
gpu: GPU type to attach to the build container.
|
|
2545
2554
|
add_python: Standalone Python version to add when the Dockerfile does not install Python.
|
|
2546
|
-
build_args: Dockerfile ``ARG`` values forwarded to the build.
|
|
2547
|
-
ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
|
|
2548
2555
|
|
|
2549
2556
|
Returns:
|
|
2550
2557
|
An `Image` built from the Dockerfile plus Modal runtime dependencies.
|
|
@@ -2590,6 +2597,12 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2590
2597
|
add_python="3.12",
|
|
2591
2598
|
ignore=FilePatternMatcher.from_file("/path/to/dockerignore"),
|
|
2592
2599
|
)
|
|
2600
|
+
|
|
2601
|
+
# Pull the Dockerfile's base image from a private registry.
|
|
2602
|
+
image = modal.Image.from_dockerfile(
|
|
2603
|
+
"./Dockerfile",
|
|
2604
|
+
registry_secret=modal.Secret.from_name("my-registry-secret"),
|
|
2605
|
+
)
|
|
2593
2606
|
```
|
|
2594
2607
|
"""
|
|
2595
2608
|
|
|
@@ -2602,7 +2615,15 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2602
2615
|
ignore=ignore, dockerfile_path=Path(path), context_dir=context_dir
|
|
2603
2616
|
)
|
|
2604
2617
|
|
|
2605
|
-
|
|
2618
|
+
registry_config = (
|
|
2619
|
+
_ImageRegistryConfig(secret=registry_secret, infer_auth_type=True) if registry_secret is not None else None
|
|
2620
|
+
)
|
|
2621
|
+
|
|
2622
|
+
def dockerfile_image(
|
|
2623
|
+
commands: list[str],
|
|
2624
|
+
base_images: dict[str, _Image] | None = None,
|
|
2625
|
+
image_registry_config: _ImageRegistryConfig | None = None,
|
|
2626
|
+
) -> _Image:
|
|
2606
2627
|
return _Image._from_args(
|
|
2607
2628
|
base_images=base_images,
|
|
2608
2629
|
dockerfile_function=lambda version: DockerfileSpec(commands=commands, context_files={}),
|
|
@@ -2610,6 +2631,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2610
2631
|
gpu_config=gpu_config,
|
|
2611
2632
|
secrets=secrets,
|
|
2612
2633
|
build_args=build_args,
|
|
2634
|
+
image_registry_config=image_registry_config,
|
|
2613
2635
|
force_build=force_build,
|
|
2614
2636
|
)
|
|
2615
2637
|
|
|
@@ -2648,19 +2670,23 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2648
2670
|
)
|
|
2649
2671
|
with open(os.path.expanduser(path)) as f:
|
|
2650
2672
|
dockerfile_lines = f.read().split("\n")
|
|
2673
|
+
if registry_secret is not None and is_multi_stage_dockerfile(dockerfile_lines):
|
|
2674
|
+
raise InvalidError("`registry_secret` is not supported with multi-stage Dockerfiles.")
|
|
2651
2675
|
|
|
2652
2676
|
# Builder versions after 2025.06 put the FROM line of a single-stage Dockerfile in its own image.
|
|
2653
2677
|
# Dockerfiles the split could change (see splittable_from_line_index) stay in one layer.
|
|
2654
2678
|
from_index = splittable_from_line_index(dockerfile_lines) if builder_version > "2025.06" else None
|
|
2655
2679
|
if from_index is None:
|
|
2656
|
-
image = dockerfile_image(dockerfile_lines)
|
|
2680
|
+
image = dockerfile_image(dockerfile_lines, image_registry_config=registry_config)
|
|
2657
2681
|
else:
|
|
2658
2682
|
# Drop the lines above FROM so Dockerfiles that differ only in header comments or blank lines
|
|
2659
2683
|
# share the cached image. Those lines can only be comments or blank lines, since
|
|
2660
2684
|
# splittable_from_line_index rejects parser directives.
|
|
2661
2685
|
from_lines = [dockerfile_lines[from_index]]
|
|
2686
|
+
# Only this layer pulls from the registry; the layers above it build on top of it.
|
|
2662
2687
|
from_image = _Image._from_args(
|
|
2663
2688
|
dockerfile_function=lambda version: DockerfileSpec(commands=from_lines, context_files={}),
|
|
2689
|
+
image_registry_config=registry_config,
|
|
2664
2690
|
force_build=force_build,
|
|
2665
2691
|
)
|
|
2666
2692
|
rest = dockerfile_lines[from_index + 1 :]
|
|
@@ -79,6 +79,15 @@ _FROM_ALIAS_PATTERN = re.compile(r"\sAS\s+\S+\s*$", re.IGNORECASE)
|
|
|
79
79
|
_PARSER_DIRECTIVE_PATTERN = re.compile(r"^#\s*[a-zA-Z][a-zA-Z0-9]*\s*=")
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def is_multi_stage_dockerfile(dockerfile_lines: Sequence[str]) -> bool:
|
|
83
|
+
"""Return whether more than one line starts with a FROM instruction.
|
|
84
|
+
|
|
85
|
+
Can have false-positives for Dockerfiles with a Heredoc bodies of continuation lines
|
|
86
|
+
that contain "from" tokens.
|
|
87
|
+
"""
|
|
88
|
+
return sum(1 for line in dockerfile_lines if _FROM_PATTERN.match(line)) > 1
|
|
89
|
+
|
|
90
|
+
|
|
82
91
|
def splittable_from_line_index(dockerfile_lines: Sequence[str]) -> int | None:
|
|
83
92
|
"""Return the line index of the FROM instruction if it can be built as its own image.
|
|
84
93
|
|
|
@@ -96,7 +105,7 @@ def splittable_from_line_index(dockerfile_lines: Sequence[str]) -> int | None:
|
|
|
96
105
|
return None
|
|
97
106
|
if any(_PARSER_DIRECTIVE_PATTERN.match(line.strip()) for line in dockerfile_lines[:start]):
|
|
98
107
|
return None
|
|
99
|
-
if
|
|
108
|
+
if is_multi_stage_dockerfile(dockerfile_lines):
|
|
100
109
|
return None
|
|
101
110
|
return start
|
|
102
111
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
aiohappyeyeballs==2.7.1
|
|
2
|
+
aiohttp==3.14.5
|
|
3
|
+
aiosignal==1.4.0
|
|
4
|
+
attrs==26.1.0
|
|
5
|
+
cbor2==5.9.0
|
|
6
|
+
certifi==2026.10.19
|
|
7
|
+
frozenlist==1.8.0
|
|
8
|
+
grpclib==0.4.9
|
|
9
|
+
h2==4.4.1
|
|
10
|
+
hpack==4.2.0
|
|
11
|
+
hyperframe==6.1.0
|
|
12
|
+
idna==3.20
|
|
13
|
+
multidict==7.1.0
|
|
14
|
+
propcache==0.5.4
|
|
15
|
+
protobuf==7.36.2
|
|
16
|
+
typing_extensions==4.16.0
|
|
17
|
+
yarl==1.25.1
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"debian": {
|
|
3
3
|
"PREVIEW": "trixie",
|
|
4
|
+
"2026.10": "trixie",
|
|
4
5
|
"2025.06": "bookworm",
|
|
5
6
|
"2024.10": "bookworm",
|
|
6
7
|
"2024.04": "bookworm",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
},
|
|
39
40
|
"micromamba": {
|
|
40
41
|
"PREVIEW": "2.9.0-debian13-slim",
|
|
42
|
+
"2026.10": "2.9.0-debian13-slim",
|
|
41
43
|
"2025.06": "2.1.1-debian12-slim",
|
|
42
44
|
"2024.10": "1.5.10-bookworm-slim",
|
|
43
45
|
"2024.04": "1.5.8-bookworm-slim",
|
|
@@ -45,6 +47,7 @@
|
|
|
45
47
|
},
|
|
46
48
|
"package_tools": {
|
|
47
49
|
"PREVIEW": "pip wheel uv",
|
|
50
|
+
"2026.10": "pip wheel uv",
|
|
48
51
|
"2025.06": "pip wheel uv",
|
|
49
52
|
"2024.10": "pip wheel uv",
|
|
50
53
|
"2024.04": "pip wheel uv",
|
|
@@ -260,26 +260,35 @@ async def list_(*, json: bool = False, env: Optional[str] = None):
|
|
|
260
260
|
@click.option(
|
|
261
261
|
"--name",
|
|
262
262
|
default=None,
|
|
263
|
-
help="Endpoint name.
|
|
263
|
+
help="Endpoint name. If omitted, Modal chooses a name.",
|
|
264
264
|
)
|
|
265
265
|
@click.option(
|
|
266
266
|
"--model",
|
|
267
267
|
required=True,
|
|
268
268
|
help="Hugging Face repo ID for the base model architecture (e.g., 'Qwen/Qwen3.6-27B-FP8').",
|
|
269
269
|
)
|
|
270
|
+
@click.option(
|
|
271
|
+
"--shared",
|
|
272
|
+
is_flag=True,
|
|
273
|
+
default=False,
|
|
274
|
+
help="Create a Shared Endpoint. Defaults to a Dedicated Endpoint.",
|
|
275
|
+
)
|
|
270
276
|
@click.option(
|
|
271
277
|
"--routing-region",
|
|
272
278
|
"routing_region",
|
|
273
279
|
multiple=True,
|
|
274
280
|
callback=_single_routing_region_callback,
|
|
275
|
-
help=
|
|
281
|
+
help=(
|
|
282
|
+
f"Region to route inference requests through. Defaults to {_DEFAULT_ROUTING_REGION}. "
|
|
283
|
+
"Shared Endpoint routing cannot currently be customized."
|
|
284
|
+
),
|
|
276
285
|
)
|
|
277
286
|
@click.option(
|
|
278
287
|
"--compute-region",
|
|
279
288
|
"compute_regions",
|
|
280
289
|
multiple=True,
|
|
281
290
|
help=(
|
|
282
|
-
"Region to run Endpoint containers in. May be specified multiple times. "
|
|
291
|
+
"Region to run Dedicated Endpoint containers in. May be specified multiple times. "
|
|
283
292
|
"This incurs a region selection price multiplier."
|
|
284
293
|
),
|
|
285
294
|
)
|
|
@@ -287,13 +296,16 @@ async def list_(*, json: bool = False, env: Optional[str] = None):
|
|
|
287
296
|
"--colocate-compute",
|
|
288
297
|
is_flag=True,
|
|
289
298
|
default=False,
|
|
290
|
-
help=
|
|
299
|
+
help=(
|
|
300
|
+
"Run all Dedicated Endpoint containers within the routing region. "
|
|
301
|
+
"This incurs a region selection price multiplier."
|
|
302
|
+
),
|
|
291
303
|
)
|
|
292
304
|
@click.option(
|
|
293
305
|
"--unauthenticated",
|
|
294
306
|
is_flag=True,
|
|
295
307
|
default=False,
|
|
296
|
-
help="Allow unauthenticated HTTP requests to
|
|
308
|
+
help="Allow unauthenticated HTTP requests to a Dedicated Endpoint.",
|
|
297
309
|
)
|
|
298
310
|
@click.option("--custom-hf-repo", default=None, help="Hugging Face repo ID for fine-tuned model weights.")
|
|
299
311
|
@click.option("--custom-hf-revision", default=None, help="Git revision for --custom-hf-repo.")
|
|
@@ -304,6 +316,7 @@ async def list_(*, json: bool = False, env: Optional[str] = None):
|
|
|
304
316
|
async def create(
|
|
305
317
|
name: Optional[str],
|
|
306
318
|
model: Optional[str] = None,
|
|
319
|
+
shared: bool = False,
|
|
307
320
|
routing_region: str = _DEFAULT_ROUTING_REGION,
|
|
308
321
|
compute_regions: tuple[str, ...] = (),
|
|
309
322
|
colocate_compute: bool = False,
|
|
@@ -317,13 +330,21 @@ async def create(
|
|
|
317
330
|
):
|
|
318
331
|
"""Deploy a new Endpoint.
|
|
319
332
|
|
|
333
|
+
Creates a Dedicated Endpoint by default, with support for custom weights and compute placement.
|
|
334
|
+
Use --shared to serve a supported base model with Modal-managed capacity.
|
|
335
|
+
|
|
320
336
|
Examples:
|
|
321
337
|
|
|
322
|
-
Create
|
|
338
|
+
Create a Dedicated Endpoint from a base model:
|
|
323
339
|
```bash
|
|
324
340
|
modal endpoint create --model Qwen/Qwen3.6-27B-FP8
|
|
325
341
|
```
|
|
326
342
|
|
|
343
|
+
Create a Shared Endpoint from a supported base model:
|
|
344
|
+
```bash
|
|
345
|
+
modal endpoint create --shared --model moonshotai/Kimi-K3
|
|
346
|
+
```
|
|
347
|
+
|
|
327
348
|
Create an Endpoint with an explicit name:
|
|
328
349
|
```bash
|
|
329
350
|
modal endpoint create --name qwen-chat --model Qwen/Qwen3.6-27B-FP8
|
|
@@ -348,6 +369,22 @@ async def create(
|
|
|
348
369
|
```
|
|
349
370
|
|
|
350
371
|
"""
|
|
372
|
+
if shared:
|
|
373
|
+
for option, provided in (
|
|
374
|
+
("--compute-region", bool(compute_regions)),
|
|
375
|
+
("--colocate-compute", colocate_compute),
|
|
376
|
+
("--unauthenticated", unauthenticated),
|
|
377
|
+
("--custom-hf-repo", custom_hf_repo is not None),
|
|
378
|
+
("--custom-hf-revision", custom_hf_revision is not None),
|
|
379
|
+
("--custom-hf-token", custom_hf_token is not None),
|
|
380
|
+
("--custom-volume-name", custom_volume_name is not None),
|
|
381
|
+
("--custom-volume-path", custom_volume_path is not None),
|
|
382
|
+
):
|
|
383
|
+
if provided:
|
|
384
|
+
raise click.UsageError(f"{option} cannot be used with --shared.")
|
|
385
|
+
if routing_region != _DEFAULT_ROUTING_REGION:
|
|
386
|
+
raise click.UsageError(f"Shared Endpoints require --routing-region {_DEFAULT_ROUTING_REGION}.")
|
|
387
|
+
|
|
351
388
|
if compute_regions and colocate_compute:
|
|
352
389
|
raise click.UsageError("--compute-region and --colocate-compute are mutually exclusive.")
|
|
353
390
|
if custom_hf_repo and custom_volume_name:
|
|
@@ -403,13 +440,18 @@ async def create(
|
|
|
403
440
|
model=model_source,
|
|
404
441
|
environment_name=environment_name,
|
|
405
442
|
unauthenticated=unauthenticated,
|
|
443
|
+
serving_mode=(api_pb2.ENDPOINT_SERVING_MODE_SHARED if shared else api_pb2.ENDPOINT_SERVING_MODE_DEDICATED),
|
|
406
444
|
)
|
|
407
445
|
if name:
|
|
408
446
|
req.name = name
|
|
409
447
|
resp = await client._stub.EndpointCreate(req)
|
|
410
448
|
|
|
411
449
|
output = OutputManager.get()
|
|
412
|
-
|
|
450
|
+
serving_mode = "Shared" if shared else "Dedicated"
|
|
451
|
+
output.print(
|
|
452
|
+
f"[green]✓[/green] {serving_mode} Endpoint '{resp.name}' ({resp.endpoint_id}) "
|
|
453
|
+
"was created and started provisioning."
|
|
454
|
+
)
|
|
413
455
|
if resp.model_name:
|
|
414
456
|
output.print(f' → Send [cyan]"model": "{resp.model_name}"[/cyan] in requests to the Endpoint.')
|
|
415
457
|
if resp.endpoint_page_url:
|
|
@@ -33,7 +33,7 @@ async def list_(json: bool | None = False):
|
|
|
33
33
|
profiles = config_profiles()
|
|
34
34
|
lookup_coros = [
|
|
35
35
|
_lookup_workspace(
|
|
36
|
-
config.get("server_url", profile=profile),
|
|
36
|
+
config.get("server_url", profile=profile, use_env=False),
|
|
37
37
|
token_id=config.get("token_id", profile=profile, use_env=False),
|
|
38
38
|
token_secret=config.get("token_secret", profile=profile, use_env=False),
|
|
39
39
|
oauth_refresh_token=config.get("oauth_refresh_token", profile=profile, use_env=False),
|
|
@@ -471,7 +471,7 @@ def service_users_list(json: bool):
|
|
|
471
471
|
|
|
472
472
|
@service_users_cli.command("delete", no_args_is_help=True)
|
|
473
473
|
@click.argument("name")
|
|
474
|
-
@click.option("--allow-missing", is_flag=True, default=False, help="Don't error if the
|
|
474
|
+
@click.option("--allow-missing", is_flag=True, default=False, help="Don't error if the Service User doesn't exist.")
|
|
475
475
|
@yes_option
|
|
476
476
|
def service_users_delete(name: str, *, allow_missing: bool, yes: bool):
|
|
477
477
|
"""Delete a Service User in this Workspace.
|
|
@@ -87,7 +87,7 @@ class _Client:
|
|
|
87
87
|
server_url: str,
|
|
88
88
|
client_type: int,
|
|
89
89
|
credentials: typing.Optional[tuple[str, str]],
|
|
90
|
-
version: str = "1.6.3.
|
|
90
|
+
version: str = "1.6.3.dev13",
|
|
91
91
|
*,
|
|
92
92
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
93
93
|
):
|
|
@@ -304,7 +304,7 @@ class Client:
|
|
|
304
304
|
server_url: str,
|
|
305
305
|
client_type: int,
|
|
306
306
|
credentials: typing.Optional[tuple[str, str]],
|
|
307
|
-
version: str = "1.6.3.
|
|
307
|
+
version: str = "1.6.3.dev13",
|
|
308
308
|
*,
|
|
309
309
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
310
310
|
):
|
|
@@ -65,7 +65,8 @@ class Image(modal.object.Object):
|
|
|
65
65
|
base_images: typing.Optional[dict[str, Image]] = None,
|
|
66
66
|
dockerfile_function: typing.Optional[
|
|
67
67
|
collections.abc.Callable[
|
|
68
|
-
[typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]],
|
|
68
|
+
[typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"]],
|
|
69
|
+
modal._image.DockerfileSpec,
|
|
69
70
|
]
|
|
70
71
|
] = None,
|
|
71
72
|
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
@@ -75,7 +76,7 @@ class Image(modal.object.Object):
|
|
|
75
76
|
image_registry_config: typing.Optional[modal._image._ImageRegistryConfig] = None,
|
|
76
77
|
context_mount_function: typing.Optional[
|
|
77
78
|
collections.abc.Callable[
|
|
78
|
-
[typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]],
|
|
79
|
+
[typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"]],
|
|
79
80
|
typing.Optional[modal.mount.Mount],
|
|
80
81
|
]
|
|
81
82
|
] = None,
|
|
@@ -873,7 +874,7 @@ class Image(modal.object.Object):
|
|
|
873
874
|
@staticmethod
|
|
874
875
|
def _registry_setup_commands(
|
|
875
876
|
tag: str,
|
|
876
|
-
builder_version: typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"],
|
|
877
|
+
builder_version: typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"],
|
|
877
878
|
setup_commands: list[str],
|
|
878
879
|
add_python: typing.Optional[str] = None,
|
|
879
880
|
) -> list[str]: ...
|
|
@@ -1043,30 +1044,35 @@ class Image(modal.object.Object):
|
|
|
1043
1044
|
*,
|
|
1044
1045
|
force_build: bool = False,
|
|
1045
1046
|
context_dir: typing.Union[pathlib.Path, str, None] = None,
|
|
1046
|
-
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1047
|
-
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
1048
|
-
gpu: typing.Optional[str] = None,
|
|
1049
|
-
add_python: typing.Optional[str] = None,
|
|
1050
|
-
build_args: dict[str, str] = {},
|
|
1051
1047
|
ignore: typing.Union[
|
|
1052
1048
|
collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
|
|
1053
1049
|
] = modal._image.AUTO_DOCKERIGNORE,
|
|
1050
|
+
build_args: dict[str, str] = {},
|
|
1051
|
+
registry_secret: typing.Optional[modal.secret.Secret] = None,
|
|
1052
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
1053
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1054
|
+
gpu: typing.Optional[str] = None,
|
|
1055
|
+
add_python: typing.Optional[str] = None,
|
|
1054
1056
|
) -> Image:
|
|
1055
1057
|
"""Build a Modal image from a local Dockerfile.
|
|
1056
1058
|
|
|
1057
1059
|
If your Dockerfile does not have Python installed, you can use the `add_python` parameter
|
|
1058
1060
|
to specify a version of Python to add to the image.
|
|
1059
1061
|
|
|
1062
|
+
A `registry_secret` can be provided to pull the base image from a private registry.
|
|
1063
|
+
Currently this is supported only for single-stage Dockerfiles.
|
|
1064
|
+
|
|
1060
1065
|
Args:
|
|
1061
1066
|
path: Path to the Dockerfile on the local machine.
|
|
1062
1067
|
force_build: If True, skip cached image builds.
|
|
1063
1068
|
context_dir: Build context directory for resolving relative COPY paths.
|
|
1069
|
+
ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
|
|
1070
|
+
build_args: Dockerfile ``ARG`` values forwarded to the build.
|
|
1071
|
+
registry_secret: Optional Secret with credentials for pulling the base image from a private registry.
|
|
1072
|
+
secrets: Secrets injected as environment variables in the build container.
|
|
1064
1073
|
env: Environment variables set in the build container.
|
|
1065
|
-
|
|
1066
|
-
gpu: GPU type to attach to the builder container.
|
|
1074
|
+
gpu: GPU type to attach to the build container.
|
|
1067
1075
|
add_python: Standalone Python version to add when the Dockerfile does not install Python.
|
|
1068
|
-
build_args: Dockerfile ``ARG`` values forwarded to the build.
|
|
1069
|
-
ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
|
|
1070
1076
|
|
|
1071
1077
|
Returns:
|
|
1072
1078
|
An `Image` built from the Dockerfile plus Modal runtime dependencies.
|
|
@@ -1112,6 +1118,12 @@ class Image(modal.object.Object):
|
|
|
1112
1118
|
add_python="3.12",
|
|
1113
1119
|
ignore=FilePatternMatcher.from_file("/path/to/dockerignore"),
|
|
1114
1120
|
)
|
|
1121
|
+
|
|
1122
|
+
# Pull the Dockerfile's base image from a private registry.
|
|
1123
|
+
image = modal.Image.from_dockerfile(
|
|
1124
|
+
"./Dockerfile",
|
|
1125
|
+
registry_secret=modal.Secret.from_name("my-registry-secret"),
|
|
1126
|
+
)
|
|
1115
1127
|
```
|
|
1116
1128
|
"""
|
|
1117
1129
|
...
|
|
@@ -369,36 +369,48 @@ class _StreamReaderThroughCommandRouterBase(_StreamReaderImpl[T]):
|
|
|
369
369
|
"""Open the underlying stream at the current offset."""
|
|
370
370
|
raise NotImplementedError
|
|
371
371
|
|
|
372
|
+
def _current_stream(self) -> AsyncGenerator[T, None]:
|
|
373
|
+
if self._stream is None:
|
|
374
|
+
self._stream = self._iterate_stream()
|
|
375
|
+
return self._stream
|
|
376
|
+
|
|
372
377
|
async def __anext__(self) -> T:
|
|
373
378
|
async with self._lock:
|
|
374
|
-
|
|
375
|
-
self._stream = self._iterate_stream()
|
|
376
|
-
return await self._stream.__anext__()
|
|
379
|
+
return await self._current_stream().__anext__()
|
|
377
380
|
|
|
378
381
|
async def _consume_stream(self) -> AsyncGenerator[T, None]:
|
|
382
|
+
# An iterator abandoned mid-loop is finalized at an arbitrary later
|
|
383
|
+
# time, so it must only close the stream it pulled from, never one a
|
|
384
|
+
# later iterator has since reopened.
|
|
385
|
+
stream: AsyncGenerator[T, None] | None = None
|
|
379
386
|
try:
|
|
380
387
|
while True:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
388
|
+
async with self._lock:
|
|
389
|
+
stream = self._current_stream()
|
|
390
|
+
try:
|
|
391
|
+
item = await stream.__anext__()
|
|
392
|
+
except StopAsyncIteration:
|
|
393
|
+
return
|
|
385
394
|
yield item
|
|
386
395
|
finally:
|
|
387
|
-
await self.
|
|
396
|
+
await self._close_stream(stream)
|
|
388
397
|
|
|
389
398
|
def __aiter__(self) -> AsyncGenerator[T, None]:
|
|
390
399
|
return self._consume_stream()
|
|
391
400
|
|
|
392
|
-
async def
|
|
393
|
-
|
|
394
|
-
pulling from is left open for that reader to close."""
|
|
395
|
-
if self._lock.locked():
|
|
401
|
+
async def _close_stream(self, stream: AsyncGenerator[T, None] | None) -> None:
|
|
402
|
+
if stream is None or self._lock.locked():
|
|
396
403
|
return
|
|
397
404
|
async with self._lock:
|
|
398
|
-
|
|
399
|
-
|
|
405
|
+
if self._stream is stream:
|
|
406
|
+
self._stream = None
|
|
400
407
|
await stream.aclose()
|
|
401
408
|
|
|
409
|
+
async def aclose(self) -> None:
|
|
410
|
+
"""Close the underlying stream. A stream another reader is currently
|
|
411
|
+
pulling from is left open for that reader to close."""
|
|
412
|
+
await self._close_stream(self._stream)
|
|
413
|
+
|
|
402
414
|
|
|
403
415
|
class _BytesStreamReaderThroughCommandRouter(_StreamReaderThroughCommandRouterBase[bytes]):
|
|
404
416
|
"""StreamReader that yields raw bytes from the router-backed stdio source
|
|
@@ -243,9 +243,11 @@ class _StreamReaderThroughCommandRouterBase(_StreamReaderImpl[T]):
|
|
|
243
243
|
"""Open the underlying stream at the current offset."""
|
|
244
244
|
...
|
|
245
245
|
|
|
246
|
+
def _current_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
|
|
246
247
|
async def __anext__(self) -> T: ...
|
|
247
248
|
def _consume_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
|
|
248
249
|
def __aiter__(self) -> collections.abc.AsyncGenerator[T, None]: ...
|
|
250
|
+
async def _close_stream(self, stream: typing.Optional[collections.abc.AsyncGenerator[T, None]]) -> None: ...
|
|
249
251
|
async def aclose(self) -> None:
|
|
250
252
|
"""Close the underlying stream. A stream another reader is currently
|
|
251
253
|
pulling from is left open for that reader to close.
|
|
@@ -63,6 +63,15 @@ PYTHON_STANDALONE_VERSIONS: dict[str, dict[str, tuple[str, str]]] = {
|
|
|
63
63
|
"3.15": ("20261021", "3.15.1"),
|
|
64
64
|
"3.15t": ("20261021", "3.15.1t"),
|
|
65
65
|
},
|
|
66
|
+
"2026.10": {
|
|
67
|
+
"3.11": ("20261021", "3.11.17"),
|
|
68
|
+
"3.12": ("20261021", "3.12.15"),
|
|
69
|
+
"3.13": ("20261021", "3.13.17"),
|
|
70
|
+
"3.14": ("20261021", "3.14.9"),
|
|
71
|
+
"3.14t": ("20261021", "3.14.9t"),
|
|
72
|
+
"3.15": ("20261021", "3.15.1"),
|
|
73
|
+
"3.15t": ("20261021", "3.15.1t"),
|
|
74
|
+
},
|
|
66
75
|
"2025.06": _PYTHON_STANDALONE_VERSIONS_2025_06,
|
|
67
76
|
"2024.10": _PYTHON_STANDALONE_VERSIONS_2025_06,
|
|
68
77
|
"2024.04": _PYTHON_STANDALONE_VERSIONS_2025_06,
|
|
@@ -303,6 +303,10 @@ class ModalClientBase(abc.ABC):
|
|
|
303
303
|
async def EndpointGetInfo(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointGetInfoRequest, modal_proto.api_pb2.EndpointGetInfoResponse]') -> None:
|
|
304
304
|
pass
|
|
305
305
|
|
|
306
|
+
@abc.abstractmethod
|
|
307
|
+
async def EndpointGetLibrary(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointGetLibraryRequest, modal_proto.api_pb2.EndpointGetLibraryResponse]') -> None:
|
|
308
|
+
pass
|
|
309
|
+
|
|
306
310
|
@abc.abstractmethod
|
|
307
311
|
async def EndpointGetLifecycle(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointGetLifecycleRequest, modal_proto.api_pb2.EndpointGetLifecycleResponse]') -> None:
|
|
308
312
|
pass
|
|
@@ -1547,6 +1551,12 @@ class ModalClientBase(abc.ABC):
|
|
|
1547
1551
|
modal_proto.api_pb2.EndpointGetInfoRequest,
|
|
1548
1552
|
modal_proto.api_pb2.EndpointGetInfoResponse,
|
|
1549
1553
|
),
|
|
1554
|
+
'/modal.client.ModalClient/EndpointGetLibrary': grpclib.const.Handler(
|
|
1555
|
+
self.EndpointGetLibrary,
|
|
1556
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1557
|
+
modal_proto.api_pb2.EndpointGetLibraryRequest,
|
|
1558
|
+
modal_proto.api_pb2.EndpointGetLibraryResponse,
|
|
1559
|
+
),
|
|
1550
1560
|
'/modal.client.ModalClient/EndpointGetLifecycle': grpclib.const.Handler(
|
|
1551
1561
|
self.EndpointGetLifecycle,
|
|
1552
1562
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
@@ -3203,6 +3213,12 @@ class ModalClientStub:
|
|
|
3203
3213
|
modal_proto.api_pb2.EndpointGetInfoRequest,
|
|
3204
3214
|
modal_proto.api_pb2.EndpointGetInfoResponse,
|
|
3205
3215
|
)
|
|
3216
|
+
self.EndpointGetLibrary = grpclib.client.UnaryUnaryMethod(
|
|
3217
|
+
channel,
|
|
3218
|
+
'/modal.client.ModalClient/EndpointGetLibrary',
|
|
3219
|
+
modal_proto.api_pb2.EndpointGetLibraryRequest,
|
|
3220
|
+
modal_proto.api_pb2.EndpointGetLibraryResponse,
|
|
3221
|
+
)
|
|
3206
3222
|
self.EndpointGetLifecycle = grpclib.client.UnaryUnaryMethod(
|
|
3207
3223
|
channel,
|
|
3208
3224
|
'/modal.client.ModalClient/EndpointGetLifecycle',
|