modal 1.4.4.dev20__tar.gz → 1.4.4.dev21__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.4.4.dev20 → modal-1.4.4.dev21}/PKG-INFO +1 -1
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_functions.py +28 -9
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_server.py +10 -2
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/app.py +4 -4
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/app.pyi +2 -2
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/entry_point.py +2 -0
- modal-1.4.4.dev21/modal/cli/image.py +110 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/client.pyi +2 -2
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/functions.pyi +32 -10
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/server.pyi +16 -2
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal.egg-info/SOURCES.txt +1 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/gen_cli_docs.py +11 -5
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/api_grpc.py +32 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/api_pb2.py +879 -810
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/api_pb2.pyi +173 -8
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/api_pb2_grpc.py +66 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/api_pb2_grpc.pyi +20 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/modal_api_grpc.py +2 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_version/__init__.py +1 -1
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/LICENSE +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/README.md +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/__main__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_billing.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_clustered_functions.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_container_entrypoint.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_environments.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_function_variants.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_grpc_client.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_image.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_ipython.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_load_context.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_location.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_logs.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_object.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_output/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_output/manager.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_output/pty.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_output/rich.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_output/status.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_partial_function.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_resolver.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_resources.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/execution_context.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_serialization.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_traceback.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_tunnel.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_tunnel.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_type_manager.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/async_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/blob_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/deprecation.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/docker_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/function_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/logger.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/mount_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/name_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_vendor/version.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_watcher.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/_workspace.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/billing.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/README.md +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/builder/base-images.json +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/call_graph.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/_download.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/_help.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/_traceback.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/app.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/billing.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/bootstrap.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/changelog.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/cluster.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/config.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/container.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/dashboard.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/dict.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/environment.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/import_refs.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/launch.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/logo.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/network_file_system.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/profile.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/queues.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/run.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/secret.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/selector.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/shell.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/skills.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/token.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/utils.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cli/volume.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/client.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cls.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/cls.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/config.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/container_process.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/container_process.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/dict.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/dict.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/environments.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/environments.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/exception.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/experimental/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/experimental/flash.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/experimental/flash.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/experimental/ipython.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/file_io.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/file_io.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/file_pattern_matcher.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/functions.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/image.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/image.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/io_streams.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/io_streams.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/mount.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/mount.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/network_file_system.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/network_file_system.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/object.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/object.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/output.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/parallel_map.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/parallel_map.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/partial_function.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/partial_function.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/proxy.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/proxy.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/py.typed +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/queue.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/queue.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/retries.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/runner.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/runner.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/running_app.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/sandbox.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/sandbox.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/sandbox_fs.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/sandbox_fs.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/schedule.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/scheduler_placement.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/secret.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/secret.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/server.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/serving.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/serving.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/snapshot.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/snapshot.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/stream_type.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/token_flow.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/token_flow.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/volume.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/volume.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/workspace.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal/workspace.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal.egg-info/requires.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_docs/mdmd/types.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/__init__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/py.typed +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/modal_version/__main__.py +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/pyproject.toml +0 -0
- {modal-1.4.4.dev20 → modal-1.4.4.dev21}/setup.cfg +0 -0
|
@@ -934,7 +934,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
934
934
|
if app and app.name:
|
|
935
935
|
app_name = app.name
|
|
936
936
|
|
|
937
|
-
#
|
|
937
|
+
# TODO: client-side `mount_client_dependencies` can be removed after 2026-06-15
|
|
938
|
+
# Kept so that the function definition proto stays identical across deploys
|
|
939
|
+
# (removing it would change the proto, creating a new app version)
|
|
938
940
|
mount_client_dependencies = False
|
|
939
941
|
if image._metadata is not None:
|
|
940
942
|
mount_client_dependencies = image._metadata.image_builder_version > "2024.10"
|
|
@@ -1149,6 +1151,28 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1149
1151
|
|
|
1150
1152
|
return obj
|
|
1151
1153
|
|
|
1154
|
+
async def _update_autoscaler(
|
|
1155
|
+
self,
|
|
1156
|
+
*,
|
|
1157
|
+
min_containers: int | None = None,
|
|
1158
|
+
max_containers: int | None = None,
|
|
1159
|
+
buffer_containers: int | None = None,
|
|
1160
|
+
scaledown_window: int | None = None,
|
|
1161
|
+
target_concurrency: int | None = None,
|
|
1162
|
+
) -> None:
|
|
1163
|
+
settings = api_pb2.AutoscalerSettings(
|
|
1164
|
+
min_containers=min_containers,
|
|
1165
|
+
max_containers=max_containers,
|
|
1166
|
+
buffer_containers=buffer_containers,
|
|
1167
|
+
scaledown_window=scaledown_window,
|
|
1168
|
+
target_concurrency=target_concurrency,
|
|
1169
|
+
)
|
|
1170
|
+
request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
|
|
1171
|
+
await self.client.stub.FunctionUpdateSchedulingParams(request)
|
|
1172
|
+
|
|
1173
|
+
# One idea would be for FunctionUpdateScheduleParams to return the current (coalesced) settings
|
|
1174
|
+
# and then we could return them here (would need some ad hoc dataclass, which I don't love)
|
|
1175
|
+
|
|
1152
1176
|
@live_method
|
|
1153
1177
|
async def update_autoscaler(
|
|
1154
1178
|
self,
|
|
@@ -1184,24 +1208,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1184
1208
|
|
|
1185
1209
|
# Extend the scaledown window to increase the amount of time that idle containers stay alive
|
|
1186
1210
|
f.update_autoscaler(scaledown_window=300)
|
|
1187
|
-
|
|
1188
|
-
```
|
|
1211
|
+
```
|
|
1189
1212
|
|
|
1190
1213
|
"""
|
|
1214
|
+
# Assert .update_autoscaler() is not called on a method as opposed to the Object. Applicable for Cls only.
|
|
1191
1215
|
if self._is_method:
|
|
1192
1216
|
raise InvalidError("Cannot call .update_autoscaler() on a method. Call it on the class instance instead.")
|
|
1193
1217
|
|
|
1194
|
-
|
|
1218
|
+
await self._update_autoscaler(
|
|
1195
1219
|
min_containers=min_containers,
|
|
1196
1220
|
max_containers=max_containers,
|
|
1197
1221
|
buffer_containers=buffer_containers,
|
|
1198
1222
|
scaledown_window=scaledown_window,
|
|
1199
1223
|
)
|
|
1200
|
-
request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
|
|
1201
|
-
await self.client.stub.FunctionUpdateSchedulingParams(request)
|
|
1202
|
-
|
|
1203
|
-
# One idea would be for FunctionUpdateScheduleParams to return the current (coalesced) settings
|
|
1204
|
-
# and then we could return them here (would need some ad hoc dataclass, which I don't love)
|
|
1205
1224
|
|
|
1206
1225
|
@classmethod
|
|
1207
1226
|
def _from_name(
|
|
@@ -100,6 +100,7 @@ class _Server:
|
|
|
100
100
|
max_containers: int | None = None,
|
|
101
101
|
buffer_containers: int | None = None,
|
|
102
102
|
scaledown_window: int | None = None,
|
|
103
|
+
target_concurrency: int | None = None,
|
|
103
104
|
) -> None:
|
|
104
105
|
"""Override the current autoscaler behavior for this Server.
|
|
105
106
|
|
|
@@ -114,14 +115,21 @@ class _Server:
|
|
|
114
115
|
|
|
115
116
|
# Limit this Server to avoid spinning up more than 5 containers
|
|
116
117
|
server.update_autoscaler(max_containers=5)
|
|
117
|
-
|
|
118
|
+
|
|
119
|
+
# Adjust Server autoscaling to target 20 concurrent requests per replica
|
|
120
|
+
server.update_autoscaler(target_concurrency=20)
|
|
121
|
+
|
|
122
|
+
# Disable the Server autoscaling by setting target_concurrency to 0
|
|
123
|
+
server.update_autoscaler(target_concurrency=0)
|
|
124
|
+
```
|
|
118
125
|
|
|
119
126
|
"""
|
|
120
|
-
return await self._get_service_function().
|
|
127
|
+
return await self._get_service_function()._update_autoscaler(
|
|
121
128
|
min_containers=min_containers,
|
|
122
129
|
max_containers=max_containers,
|
|
123
130
|
scaledown_window=scaledown_window,
|
|
124
131
|
buffer_containers=buffer_containers,
|
|
132
|
+
target_concurrency=target_concurrency,
|
|
125
133
|
)
|
|
126
134
|
|
|
127
135
|
# ============ Hydration ============
|
|
@@ -1291,7 +1291,7 @@ class _App:
|
|
|
1291
1291
|
exit_grace_period: int = 0, # Grace period for in-flight requests on shutdown
|
|
1292
1292
|
routing_regions: list[str] = ["us-east"], # Required: Regions to deploy proxy endpoints
|
|
1293
1293
|
h2_enabled: bool = False, # Enable HTTP/2
|
|
1294
|
-
target_concurrency: int | None = None, # Target concurrency for the server
|
|
1294
|
+
target_concurrency: int | None = None, # Target concurrency for the server; 0 disables autoscaling
|
|
1295
1295
|
cloud: str | None = None, # Cloud provider (aws, gcp, oci, auto)
|
|
1296
1296
|
region: str | Sequence[str] | None = None, # Region(s) to run on
|
|
1297
1297
|
nonpreemptible: bool = False, # Whether to use non-preemptible instances
|
|
@@ -1333,7 +1333,7 @@ class _App:
|
|
|
1333
1333
|
exit_grace_period: Grace period for in-flight requests on shutdown.
|
|
1334
1334
|
routing_regions: Regions to deploy proxy endpoints.
|
|
1335
1335
|
h2_enabled: Enable HTTP/2.
|
|
1336
|
-
target_concurrency: Target concurrency for the server.
|
|
1336
|
+
target_concurrency: Target concurrency for the server; 0 disables autoscaling.
|
|
1337
1337
|
cloud: Cloud provider (aws, gcp, oci, auto).
|
|
1338
1338
|
region: Region(s) to run on.
|
|
1339
1339
|
nonpreemptible: Whether to use non-preemptible instances.
|
|
@@ -1368,8 +1368,8 @@ class _App:
|
|
|
1368
1368
|
)
|
|
1369
1369
|
|
|
1370
1370
|
if target_concurrency is not None:
|
|
1371
|
-
if not isinstance(target_concurrency, int) or target_concurrency <
|
|
1372
|
-
raise InvalidError("The `target_concurrency` argument must be a
|
|
1371
|
+
if not isinstance(target_concurrency, int) or target_concurrency < 0:
|
|
1372
|
+
raise InvalidError("The `target_concurrency` argument must be a non-negative integer.")
|
|
1373
1373
|
|
|
1374
1374
|
http_config = api_pb2.HTTPConfig(
|
|
1375
1375
|
port=port,
|
|
@@ -796,7 +796,7 @@ class _App:
|
|
|
796
796
|
exit_grace_period: Grace period for in-flight requests on shutdown.
|
|
797
797
|
routing_regions: Regions to deploy proxy endpoints.
|
|
798
798
|
h2_enabled: Enable HTTP/2.
|
|
799
|
-
target_concurrency: Target concurrency for the server.
|
|
799
|
+
target_concurrency: Target concurrency for the server; 0 disables autoscaling.
|
|
800
800
|
cloud: Cloud provider (aws, gcp, oci, auto).
|
|
801
801
|
region: Region(s) to run on.
|
|
802
802
|
nonpreemptible: Whether to use non-preemptible instances.
|
|
@@ -1792,7 +1792,7 @@ class App:
|
|
|
1792
1792
|
exit_grace_period: Grace period for in-flight requests on shutdown.
|
|
1793
1793
|
routing_regions: Regions to deploy proxy endpoints.
|
|
1794
1794
|
h2_enabled: Enable HTTP/2.
|
|
1795
|
-
target_concurrency: Target concurrency for the server.
|
|
1795
|
+
target_concurrency: Target concurrency for the server; 0 disables autoscaling.
|
|
1796
1796
|
cloud: Cloud provider (aws, gcp, oci, auto).
|
|
1797
1797
|
region: Region(s) to run on.
|
|
1798
1798
|
nonpreemptible: Whether to use non-preemptible instances.
|
|
@@ -19,6 +19,7 @@ from .container import container_cli
|
|
|
19
19
|
from .dashboard import dashboard
|
|
20
20
|
from .dict import dict_cli
|
|
21
21
|
from .environment import environment_cli
|
|
22
|
+
from .image import image_cli
|
|
22
23
|
from .launch import launch_cli
|
|
23
24
|
from .logo import print_logo
|
|
24
25
|
from .network_file_system import nfs_cli
|
|
@@ -106,6 +107,7 @@ entrypoint_cli.add_command(container_cli, panel="Deployments")
|
|
|
106
107
|
# cluster is hidden while multi-node is in beta/experimental
|
|
107
108
|
entrypoint_cli.add_command(cluster_cli, panel="Deployments", hidden=True)
|
|
108
109
|
|
|
110
|
+
entrypoint_cli.add_command(image_cli, panel="Storage")
|
|
109
111
|
entrypoint_cli.add_command(dict_cli, panel="Storage")
|
|
110
112
|
entrypoint_cli.add_command(nfs_cli, panel="Storage")
|
|
111
113
|
entrypoint_cli.add_command(secret_cli, panel="Storage")
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Copyright Modal Labs 2026
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json as json_module
|
|
7
|
+
from collections.abc import AsyncIterator
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from modal._environments import ensure_env
|
|
13
|
+
from modal._utils.async_utils import synchronizer
|
|
14
|
+
from modal._utils.time_utils import timestamp_to_localized_str
|
|
15
|
+
from modal.cli.utils import display_table, env_option
|
|
16
|
+
from modal.client import _Client
|
|
17
|
+
from modal.output import OutputManager
|
|
18
|
+
from modal_proto import api_pb2
|
|
19
|
+
|
|
20
|
+
from ._help import ModalGroup
|
|
21
|
+
|
|
22
|
+
IMAGE_REGISTRY_LIST_PAGE_SIZE = 100
|
|
23
|
+
IMAGE_REGISTRY_LIST_PAGE_DELAY_SECONDS = 1.0
|
|
24
|
+
|
|
25
|
+
image_cli = ModalGroup(name="image", help="Manage Images.")
|
|
26
|
+
image_registry_cli = ModalGroup(name="registry", help="Manage the Modal Image name/tag registry.")
|
|
27
|
+
image_cli.add_command(image_registry_cli)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _print_result_summary(count: int) -> None:
|
|
31
|
+
result_label = "result" if count == 1 else "results"
|
|
32
|
+
OutputManager.get().print(f"Showing {count} {result_label}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _tag_item_json(item: api_pb2.ImageListTagsItem) -> dict[str, str]:
|
|
36
|
+
return {
|
|
37
|
+
"tag": item.tag,
|
|
38
|
+
"image_id": item.image_id,
|
|
39
|
+
"created_at": timestamp_to_localized_str(item.created_at, True) or "-",
|
|
40
|
+
"updated_at": timestamp_to_localized_str(item.updated_at, True) or "-",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _tag_item_row(item: api_pb2.ImageListTagsItem) -> tuple[str, str, str]:
|
|
45
|
+
return (
|
|
46
|
+
item.tag,
|
|
47
|
+
item.image_id,
|
|
48
|
+
timestamp_to_localized_str(item.updated_at, False) or "-",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def _iter_tag_pages(
|
|
53
|
+
client: _Client, env: str, prefix: str
|
|
54
|
+
) -> AsyncIterator[tuple[str, list[api_pb2.ImageListTagsItem]]]:
|
|
55
|
+
page_token = ""
|
|
56
|
+
environment_name = env
|
|
57
|
+
|
|
58
|
+
while True:
|
|
59
|
+
response = await client.stub.ImageListTags(
|
|
60
|
+
api_pb2.ImageListTagsRequest(
|
|
61
|
+
environment_name=env,
|
|
62
|
+
tag_prefix=prefix,
|
|
63
|
+
max_objects=IMAGE_REGISTRY_LIST_PAGE_SIZE,
|
|
64
|
+
page_token=page_token,
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
environment_name = response.environment_name
|
|
68
|
+
yield environment_name, list(response.items)
|
|
69
|
+
|
|
70
|
+
page_token = response.next_page_token
|
|
71
|
+
if not page_token:
|
|
72
|
+
return
|
|
73
|
+
await asyncio.sleep(IMAGE_REGISTRY_LIST_PAGE_DELAY_SECONDS)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@image_registry_cli.command("list", help="List named Images.")
|
|
77
|
+
@env_option
|
|
78
|
+
@click.option("--prefix", default="", help="Only include named image tags that start with this prefix.")
|
|
79
|
+
@click.option("--json", is_flag=True, default=False)
|
|
80
|
+
@synchronizer.create_blocking
|
|
81
|
+
async def list_(
|
|
82
|
+
env: Optional[str] = None,
|
|
83
|
+
prefix: str = "",
|
|
84
|
+
json: bool = False,
|
|
85
|
+
):
|
|
86
|
+
env = ensure_env(env)
|
|
87
|
+
client = await _Client.from_env()
|
|
88
|
+
|
|
89
|
+
if json:
|
|
90
|
+
count = 0
|
|
91
|
+
click.echo("[", nl=False)
|
|
92
|
+
async for _, page_items in _iter_tag_pages(client, env, prefix):
|
|
93
|
+
for item in page_items:
|
|
94
|
+
if count:
|
|
95
|
+
click.echo(",", nl=False)
|
|
96
|
+
click.echo(json_module.dumps(_tag_item_json(item)), nl=False)
|
|
97
|
+
count += 1
|
|
98
|
+
click.echo("]")
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
count = 0
|
|
102
|
+
first_page = True
|
|
103
|
+
async for environment_name, page_items in _iter_tag_pages(client, env, prefix):
|
|
104
|
+
if first_page or page_items:
|
|
105
|
+
title = f"Named Images in environment '{environment_name}'" if first_page else ""
|
|
106
|
+
display_table(["Tag", "Image ID", "Updated at"], [_tag_item_row(item) for item in page_items], title=title)
|
|
107
|
+
first_page = False
|
|
108
|
+
count += len(page_items)
|
|
109
|
+
|
|
110
|
+
_print_result_summary(count)
|
|
@@ -35,7 +35,7 @@ class _Client:
|
|
|
35
35
|
server_url: str,
|
|
36
36
|
client_type: int,
|
|
37
37
|
credentials: typing.Optional[tuple[str, str]],
|
|
38
|
-
version: str = "1.4.4.
|
|
38
|
+
version: str = "1.4.4.dev21",
|
|
39
39
|
):
|
|
40
40
|
"""mdmd:hidden
|
|
41
41
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -205,7 +205,7 @@ class Client:
|
|
|
205
205
|
server_url: str,
|
|
206
206
|
client_type: int,
|
|
207
207
|
credentials: typing.Optional[tuple[str, str]],
|
|
208
|
-
version: str = "1.4.4.
|
|
208
|
+
version: str = "1.4.4.dev21",
|
|
209
209
|
):
|
|
210
210
|
"""mdmd:hidden
|
|
211
211
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -121,6 +121,30 @@ class Function(
|
|
|
121
121
|
"""
|
|
122
122
|
...
|
|
123
123
|
|
|
124
|
+
class ___update_autoscaler_spec(typing_extensions.Protocol):
|
|
125
|
+
def __call__(
|
|
126
|
+
self,
|
|
127
|
+
/,
|
|
128
|
+
*,
|
|
129
|
+
min_containers: typing.Optional[int] = None,
|
|
130
|
+
max_containers: typing.Optional[int] = None,
|
|
131
|
+
buffer_containers: typing.Optional[int] = None,
|
|
132
|
+
scaledown_window: typing.Optional[int] = None,
|
|
133
|
+
target_concurrency: typing.Optional[int] = None,
|
|
134
|
+
) -> None: ...
|
|
135
|
+
async def aio(
|
|
136
|
+
self,
|
|
137
|
+
/,
|
|
138
|
+
*,
|
|
139
|
+
min_containers: typing.Optional[int] = None,
|
|
140
|
+
max_containers: typing.Optional[int] = None,
|
|
141
|
+
buffer_containers: typing.Optional[int] = None,
|
|
142
|
+
scaledown_window: typing.Optional[int] = None,
|
|
143
|
+
target_concurrency: typing.Optional[int] = None,
|
|
144
|
+
) -> None: ...
|
|
145
|
+
|
|
146
|
+
_update_autoscaler: ___update_autoscaler_spec
|
|
147
|
+
|
|
124
148
|
class __update_autoscaler_spec(typing_extensions.Protocol):
|
|
125
149
|
def __call__(
|
|
126
150
|
self,
|
|
@@ -157,8 +181,7 @@ class Function(
|
|
|
157
181
|
|
|
158
182
|
# Extend the scaledown window to increase the amount of time that idle containers stay alive
|
|
159
183
|
f.update_autoscaler(scaledown_window=300)
|
|
160
|
-
|
|
161
|
-
```
|
|
184
|
+
```
|
|
162
185
|
"""
|
|
163
186
|
...
|
|
164
187
|
|
|
@@ -197,8 +220,7 @@ class Function(
|
|
|
197
220
|
|
|
198
221
|
# Extend the scaledown window to increase the amount of time that idle containers stay alive
|
|
199
222
|
f.update_autoscaler(scaledown_window=300)
|
|
200
|
-
|
|
201
|
-
```
|
|
223
|
+
```
|
|
202
224
|
"""
|
|
203
225
|
...
|
|
204
226
|
|
|
@@ -465,7 +487,7 @@ class Function(
|
|
|
465
487
|
|
|
466
488
|
_call_generator: ___call_generator_spec
|
|
467
489
|
|
|
468
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
490
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
469
491
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
470
492
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result.
|
|
471
493
|
|
|
@@ -490,7 +512,7 @@ class Function(
|
|
|
490
512
|
"""
|
|
491
513
|
...
|
|
492
514
|
|
|
493
|
-
remote: __remote_spec[modal._functions.
|
|
515
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
|
|
494
516
|
|
|
495
517
|
class __remote_gen_spec(typing_extensions.Protocol):
|
|
496
518
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -540,7 +562,7 @@ class Function(
|
|
|
540
562
|
"""
|
|
541
563
|
...
|
|
542
564
|
|
|
543
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
565
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
544
566
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
545
567
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
546
568
|
|
|
@@ -573,7 +595,7 @@ class Function(
|
|
|
573
595
|
"""
|
|
574
596
|
...
|
|
575
597
|
|
|
576
|
-
_experimental_spawn: ___experimental_spawn_spec[modal._functions.
|
|
598
|
+
_experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
|
|
577
599
|
|
|
578
600
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
|
|
579
601
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
|
|
@@ -581,7 +603,7 @@ class Function(
|
|
|
581
603
|
|
|
582
604
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
|
|
583
605
|
|
|
584
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
606
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
585
607
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
586
608
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
587
609
|
|
|
@@ -614,7 +636,7 @@ class Function(
|
|
|
614
636
|
"""
|
|
615
637
|
...
|
|
616
638
|
|
|
617
|
-
spawn: __spawn_spec[modal._functions.
|
|
639
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
|
|
618
640
|
|
|
619
641
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
620
642
|
"""Return the inner Python object wrapped by this Modal Function.
|
|
@@ -51,6 +51,7 @@ class Server:
|
|
|
51
51
|
max_containers: typing.Optional[int] = None,
|
|
52
52
|
buffer_containers: typing.Optional[int] = None,
|
|
53
53
|
scaledown_window: typing.Optional[int] = None,
|
|
54
|
+
target_concurrency: typing.Optional[int] = None,
|
|
54
55
|
) -> None:
|
|
55
56
|
"""Override the current autoscaler behavior for this Server.
|
|
56
57
|
|
|
@@ -65,7 +66,13 @@ class Server:
|
|
|
65
66
|
|
|
66
67
|
# Limit this Server to avoid spinning up more than 5 containers
|
|
67
68
|
server.update_autoscaler(max_containers=5)
|
|
68
|
-
|
|
69
|
+
|
|
70
|
+
# Adjust Server autoscaling to target 20 concurrent requests per replica
|
|
71
|
+
server.update_autoscaler(target_concurrency=20)
|
|
72
|
+
|
|
73
|
+
# Disable the Server autoscaling by setting target_concurrency to 0
|
|
74
|
+
server.update_autoscaler(target_concurrency=0)
|
|
75
|
+
```
|
|
69
76
|
"""
|
|
70
77
|
...
|
|
71
78
|
|
|
@@ -77,6 +84,7 @@ class Server:
|
|
|
77
84
|
max_containers: typing.Optional[int] = None,
|
|
78
85
|
buffer_containers: typing.Optional[int] = None,
|
|
79
86
|
scaledown_window: typing.Optional[int] = None,
|
|
87
|
+
target_concurrency: typing.Optional[int] = None,
|
|
80
88
|
) -> None:
|
|
81
89
|
"""Override the current autoscaler behavior for this Server.
|
|
82
90
|
|
|
@@ -91,7 +99,13 @@ class Server:
|
|
|
91
99
|
|
|
92
100
|
# Limit this Server to avoid spinning up more than 5 containers
|
|
93
101
|
server.update_autoscaler(max_containers=5)
|
|
94
|
-
|
|
102
|
+
|
|
103
|
+
# Adjust Server autoscaling to target 20 concurrent requests per replica
|
|
104
|
+
server.update_autoscaler(target_concurrency=20)
|
|
105
|
+
|
|
106
|
+
# Disable the Server autoscaling by setting target_concurrency to 0
|
|
107
|
+
server.update_autoscaler(target_concurrency=0)
|
|
108
|
+
```
|
|
95
109
|
"""
|
|
96
110
|
...
|
|
97
111
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright Modal Labs 2023
|
|
2
2
|
import inspect
|
|
3
|
+
import re
|
|
3
4
|
import sys
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import cast
|
|
@@ -9,6 +10,11 @@ from click import Command, Context, Group
|
|
|
9
10
|
from modal.cli.entry_point import entrypoint_cli
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
def _escape_svelte(text: str) -> str:
|
|
14
|
+
"""Wrap {placeholder} patterns in backticks so mdsvex/Svelte won't interpret them as expressions."""
|
|
15
|
+
return re.sub(r"(?<!`)\{([a-z_]+)\}(?!`)", r"`{\1}`", text)
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
# Adapted from typer_cli for generating CLI docs from click commands
|
|
13
19
|
# (see https://github.com/tiangolo/typer-cli/issues/50)
|
|
14
20
|
def get_docs_for_click(
|
|
@@ -26,7 +32,7 @@ def get_docs_for_click(
|
|
|
26
32
|
title = f"`{command_name}`" if command_name else "CLI"
|
|
27
33
|
docs += f" {title}\n\n"
|
|
28
34
|
if obj.help:
|
|
29
|
-
docs += f"{inspect.cleandoc(obj.help)}\n\n"
|
|
35
|
+
docs += f"{_escape_svelte(inspect.cleandoc(obj.help))}\n\n"
|
|
30
36
|
usage_pieces = obj.collect_usage_pieces(ctx)
|
|
31
37
|
if usage_pieces:
|
|
32
38
|
docs += "**Usage**:\n\n"
|
|
@@ -51,7 +57,7 @@ def get_docs_for_click(
|
|
|
51
57
|
for arg_name, arg_help in args:
|
|
52
58
|
docs += f"* `{arg_name}`"
|
|
53
59
|
if arg_help:
|
|
54
|
-
docs += f": {arg_help}"
|
|
60
|
+
docs += f": {_escape_svelte(arg_help)}"
|
|
55
61
|
docs += "\n"
|
|
56
62
|
docs += "\n"
|
|
57
63
|
if opts:
|
|
@@ -59,11 +65,11 @@ def get_docs_for_click(
|
|
|
59
65
|
for opt_name, opt_help in opts:
|
|
60
66
|
docs += f"* `{opt_name}`"
|
|
61
67
|
if opt_help:
|
|
62
|
-
docs += f": {opt_help}"
|
|
68
|
+
docs += f": {_escape_svelte(opt_help)}"
|
|
63
69
|
docs += "\n"
|
|
64
70
|
docs += "\n"
|
|
65
71
|
if obj.epilog:
|
|
66
|
-
docs += f"{obj.epilog}\n\n"
|
|
72
|
+
docs += f"{_escape_svelte(obj.epilog)}\n\n"
|
|
67
73
|
if isinstance(obj, Group):
|
|
68
74
|
group: Group = cast(Group, obj)
|
|
69
75
|
commands = group.list_commands(ctx)
|
|
@@ -77,7 +83,7 @@ def get_docs_for_click(
|
|
|
77
83
|
docs += f"* `{command_obj.name}`"
|
|
78
84
|
command_help = command_obj.get_short_help_str(limit=250)
|
|
79
85
|
if command_help:
|
|
80
|
-
docs += f": {command_help}"
|
|
86
|
+
docs += f": {_escape_svelte(command_help)}"
|
|
81
87
|
docs += "\n"
|
|
82
88
|
docs += "\n"
|
|
83
89
|
for command in commands:
|
|
@@ -435,10 +435,18 @@ class ModalClientBase(abc.ABC):
|
|
|
435
435
|
async def ImageJoinStreaming(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImageJoinStreamingRequest, modal_proto.api_pb2.ImageJoinStreamingResponse]') -> None:
|
|
436
436
|
pass
|
|
437
437
|
|
|
438
|
+
@abc.abstractmethod
|
|
439
|
+
async def ImageListTags(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImageListTagsRequest, modal_proto.api_pb2.ImageListTagsResponse]') -> None:
|
|
440
|
+
pass
|
|
441
|
+
|
|
438
442
|
@abc.abstractmethod
|
|
439
443
|
async def ImagePublish(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImagePublishRequest, modal_proto.api_pb2.ImagePublishResponse]') -> None:
|
|
440
444
|
pass
|
|
441
445
|
|
|
446
|
+
@abc.abstractmethod
|
|
447
|
+
async def ImageTagRevisions(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImageTagRevisionsRequest, modal_proto.api_pb2.ImageTagRevisionsResponse]') -> None:
|
|
448
|
+
pass
|
|
449
|
+
|
|
442
450
|
@abc.abstractmethod
|
|
443
451
|
async def MapAwait(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.MapAwaitRequest, modal_proto.api_pb2.MapAwaitResponse]') -> None:
|
|
444
452
|
pass
|
|
@@ -1461,12 +1469,24 @@ class ModalClientBase(abc.ABC):
|
|
|
1461
1469
|
modal_proto.api_pb2.ImageJoinStreamingRequest,
|
|
1462
1470
|
modal_proto.api_pb2.ImageJoinStreamingResponse,
|
|
1463
1471
|
),
|
|
1472
|
+
'/modal.client.ModalClient/ImageListTags': grpclib.const.Handler(
|
|
1473
|
+
self.ImageListTags,
|
|
1474
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1475
|
+
modal_proto.api_pb2.ImageListTagsRequest,
|
|
1476
|
+
modal_proto.api_pb2.ImageListTagsResponse,
|
|
1477
|
+
),
|
|
1464
1478
|
'/modal.client.ModalClient/ImagePublish': grpclib.const.Handler(
|
|
1465
1479
|
self.ImagePublish,
|
|
1466
1480
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1467
1481
|
modal_proto.api_pb2.ImagePublishRequest,
|
|
1468
1482
|
modal_proto.api_pb2.ImagePublishResponse,
|
|
1469
1483
|
),
|
|
1484
|
+
'/modal.client.ModalClient/ImageTagRevisions': grpclib.const.Handler(
|
|
1485
|
+
self.ImageTagRevisions,
|
|
1486
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1487
|
+
modal_proto.api_pb2.ImageTagRevisionsRequest,
|
|
1488
|
+
modal_proto.api_pb2.ImageTagRevisionsResponse,
|
|
1489
|
+
),
|
|
1470
1490
|
'/modal.client.ModalClient/MapAwait': grpclib.const.Handler(
|
|
1471
1491
|
self.MapAwait,
|
|
1472
1492
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
@@ -2691,12 +2711,24 @@ class ModalClientStub:
|
|
|
2691
2711
|
modal_proto.api_pb2.ImageJoinStreamingRequest,
|
|
2692
2712
|
modal_proto.api_pb2.ImageJoinStreamingResponse,
|
|
2693
2713
|
)
|
|
2714
|
+
self.ImageListTags = grpclib.client.UnaryUnaryMethod(
|
|
2715
|
+
channel,
|
|
2716
|
+
'/modal.client.ModalClient/ImageListTags',
|
|
2717
|
+
modal_proto.api_pb2.ImageListTagsRequest,
|
|
2718
|
+
modal_proto.api_pb2.ImageListTagsResponse,
|
|
2719
|
+
)
|
|
2694
2720
|
self.ImagePublish = grpclib.client.UnaryUnaryMethod(
|
|
2695
2721
|
channel,
|
|
2696
2722
|
'/modal.client.ModalClient/ImagePublish',
|
|
2697
2723
|
modal_proto.api_pb2.ImagePublishRequest,
|
|
2698
2724
|
modal_proto.api_pb2.ImagePublishResponse,
|
|
2699
2725
|
)
|
|
2726
|
+
self.ImageTagRevisions = grpclib.client.UnaryUnaryMethod(
|
|
2727
|
+
channel,
|
|
2728
|
+
'/modal.client.ModalClient/ImageTagRevisions',
|
|
2729
|
+
modal_proto.api_pb2.ImageTagRevisionsRequest,
|
|
2730
|
+
modal_proto.api_pb2.ImageTagRevisionsResponse,
|
|
2731
|
+
)
|
|
2700
2732
|
self.MapAwait = grpclib.client.UnaryUnaryMethod(
|
|
2701
2733
|
channel,
|
|
2702
2734
|
'/modal.client.ModalClient/MapAwait',
|