modal 1.6.1.dev0__tar.gz → 1.6.1.dev3__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.1.dev0 → modal-1.6.1.dev3}/PKG-INFO +3 -3
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_environments.py +2 -1
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_output/status.py +1 -3
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_server.py +34 -9
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_server.pyi +18 -1
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/grpc_utils.py +11 -12
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/function.py +5 -3
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/server.py +5 -4
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/client.pyi +2 -2
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/config.py +28 -6
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/object.pyi +0 -2
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/sandbox.py +23 -6
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/sandbox.pyi +9 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/skills/modal/SKILL.md +19 -24
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/types.py +4 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/volume.py +4 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/volume.pyi +12 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal.egg-info/PKG-INFO +3 -3
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal.egg-info/requires.txt +2 -2
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/api_pb2.py +1383 -1373
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/api_pb2.pyi +64 -8
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/task_command_router_pb2.py +96 -96
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/task_command_router_pb2.pyi +12 -2
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_version/__init__.py +1 -1
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/pyproject.toml +3 -3
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/LICENSE +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/README.md +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/__main__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_billing.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_cluster.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_cluster.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_container_entrypoint.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_function_variants.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_functions.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_grpc_client.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_image.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_ipython.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_load_context.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_location.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_logs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_logs_manager.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_logs_manager.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_object.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_outbound_policy.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_outbound_policy.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_output/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_output/manager.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_output/pty.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_output/rich.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_partial_function.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_resolver.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_resources.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/asgi.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/execution_context.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/telemetry.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_serialization.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_supports_logs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_traceback.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_tunnel.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_tunnel.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_type_manager.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/app_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/async_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/blob_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/browser_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/curl_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/deprecation.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/display_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/docker_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/function_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/git_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/hash_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/http_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/idle_countdown.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/logger.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/mount_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/name_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/package_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/proxy_support.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/shell_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_utils/time_utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_vendor/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_vendor/tblib.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_vendor/version.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_watcher.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/_workspace.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/app.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/app.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/billing.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/billing.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/2023.12.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/2024.04.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/2024.10.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/2025.06.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/README.md +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/builder/base-images.json +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/call_graph.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/_download.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/_help.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/_logs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/_stats.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/_traceback.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/app.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/billing.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/changelog.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/config.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/container.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/curl.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/dashboard.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/dict.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/endpoint.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/entry_point.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/environment.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/image.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/import_refs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/logo.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/network_file_system.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/profile.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/queues.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/run.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/secret.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/selector.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/shell.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/skills.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/token.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/utils.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/volume.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cli/workspace.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/client.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cls.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cls.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cluster.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/cluster.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/container_process.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/container_process.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/dict.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/dict.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/environments.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/environments.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/exception.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/_flash_container_entry.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/flash.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/flash.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/flash_server.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/flash_server.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/experimental/ipython.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/file_pattern_matcher.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/functions.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/functions.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/image.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/image.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/io_streams.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/io_streams.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/mount.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/mount.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/network_file_system.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/network_file_system.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/object.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/output.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/parallel_map.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/parallel_map.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/partial_function.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/partial_function.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/proxy.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/proxy.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/py.typed +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/queue.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/queue.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/retries.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/runner.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/runner.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/running_app.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/sandbox_fs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/sandbox_fs.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/schedule.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/scheduler_placement.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/secret.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/secret.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/server.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/server.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/serving.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/serving.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/snapshot.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/snapshot.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/stream_type.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/token_flow.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/token_flow.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/workspace.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal/workspace.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal.egg-info/top_level.txt +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/mdmd/types.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_docs/reference_intro.md +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/__init__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/api_grpc.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/py.typed +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/modal_version/__main__.py +0 -0
- {modal-1.6.1.dev0 → modal-1.6.1.dev3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modal
|
|
3
|
-
Version: 1.6.1.
|
|
3
|
+
Version: 1.6.1.dev3
|
|
4
4
|
Summary: Python client library for Modal
|
|
5
5
|
Author-email: Modal Labs <support@modal.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,9 +21,9 @@ Requires-Dist: certifi
|
|
|
21
21
|
Requires-Dist: click~=8.1
|
|
22
22
|
Requires-Dist: grpclib<0.4.10,>=0.4.7; python_version < "3.14"
|
|
23
23
|
Requires-Dist: grpclib<0.4.10,>=0.4.9; python_version >= "3.14"
|
|
24
|
-
Requires-Dist: protobuf!=4.24.0,<
|
|
24
|
+
Requires-Dist: protobuf!=4.24.0,<8.0,>=3.19
|
|
25
25
|
Requires-Dist: rich>=12.0.0
|
|
26
|
-
Requires-Dist: synchronicity~=0.12.
|
|
26
|
+
Requires-Dist: synchronicity~=0.12.6
|
|
27
27
|
Requires-Dist: toml
|
|
28
28
|
Requires-Dist: types-certifi
|
|
29
29
|
Requires-Dist: types-toml
|
|
@@ -22,7 +22,7 @@ from ._utils.time_utils import is_utc_month_aligned, parse_billing_cycle
|
|
|
22
22
|
from .client import _Client
|
|
23
23
|
from .config import config, logger
|
|
24
24
|
from .exception import InvalidError, WorkspaceManagementError
|
|
25
|
-
from .types import BillingReportItem, EnvironmentBillingSummary
|
|
25
|
+
from .types import AppInfo, BillingReportItem, EnvironmentBillingSummary
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
28
|
import modal
|
|
@@ -346,6 +346,7 @@ class _EnvironmentAppsManager:
|
|
|
346
346
|
self._environment.name,
|
|
347
347
|
self._environment.client,
|
|
348
348
|
description=item.description or None,
|
|
349
|
+
info=AppInfo._from_proto(item.metadata, item.app_id),
|
|
349
350
|
)
|
|
350
351
|
for item in response.apps
|
|
351
352
|
if item.state not in stopped_app_states
|
|
@@ -13,9 +13,7 @@ from .manager import OutputManager
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _get_annotation_for_web_url(url_info: api_pb2.WebUrlInfo) -> str:
|
|
16
|
-
if url_info.
|
|
17
|
-
suffix = " [grey70](label truncated)[/grey70]"
|
|
18
|
-
elif url_info.label_stolen:
|
|
16
|
+
if url_info.label_stolen:
|
|
19
17
|
suffix = " [grey70](label stolen)[/grey70]"
|
|
20
18
|
else:
|
|
21
19
|
suffix = ""
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Copyright Modal Labs 2025
|
|
2
|
+
import asyncio
|
|
2
3
|
import inspect
|
|
3
4
|
import json
|
|
5
|
+
import time
|
|
4
6
|
import typing
|
|
7
|
+
import uuid
|
|
5
8
|
from datetime import datetime, timedelta, timezone
|
|
6
9
|
|
|
7
10
|
from typing_extensions import Self
|
|
@@ -477,9 +480,8 @@ class _Server:
|
|
|
477
480
|
return ServerStats._from_proto(stats)
|
|
478
481
|
|
|
479
482
|
|
|
480
|
-
@retry(n_attempts=5, base_delay=0.5, attempt_timeout=65, total_timeout=200)
|
|
481
483
|
async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int, str, str]:
|
|
482
|
-
"""POST to a sticky session control endpoint,
|
|
484
|
+
"""POST to a sticky session control endpoint, raising on 5xx."""
|
|
483
485
|
async with ClientSessionRegistry.get_session().post(url, headers=headers) as resp:
|
|
484
486
|
body = await resp.text()
|
|
485
487
|
if resp.status >= 400:
|
|
@@ -489,6 +491,28 @@ async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int,
|
|
|
489
491
|
return resp.status, resp.reason or "", body
|
|
490
492
|
|
|
491
493
|
|
|
494
|
+
async def _post_session_start(base_url: str, fn: _Function, idle_timeout: int) -> tuple[int, str, str]:
|
|
495
|
+
idempotency_key = str(uuid.uuid4())
|
|
496
|
+
start_timestamp = str(time.time())
|
|
497
|
+
|
|
498
|
+
@retry(n_attempts=5, base_delay=0.5, attempt_timeout=None, total_timeout=1515)
|
|
499
|
+
async def attempt() -> tuple[int, str, str]:
|
|
500
|
+
headers = {
|
|
501
|
+
"Modal-Authorization": f"Bearer {await fn._get_flash_auth_token()}",
|
|
502
|
+
"x-idempotency-key": idempotency_key,
|
|
503
|
+
"x-modal-timestamp": start_timestamp,
|
|
504
|
+
"x-modal-server-session-idle-timeout": str(idle_timeout),
|
|
505
|
+
}
|
|
506
|
+
return await _post_session_control(f"{base_url}/_modal/sessions/start", headers)
|
|
507
|
+
|
|
508
|
+
return await attempt()
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@retry(n_attempts=3, base_delay=0.5, attempt_timeout=None, total_timeout=60)
|
|
512
|
+
async def _post_session_terminate(base_url: str, headers: dict[str, str]) -> tuple[int, str, str]:
|
|
513
|
+
return await _post_session_control(f"{base_url}/_modal/sessions/terminate", headers)
|
|
514
|
+
|
|
515
|
+
|
|
492
516
|
class _ServerSessionsManager:
|
|
493
517
|
"""mdmd:namespace"""
|
|
494
518
|
|
|
@@ -507,6 +531,10 @@ class _ServerSessionsManager:
|
|
|
507
531
|
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
508
532
|
for as long as it holds a live session.
|
|
509
533
|
|
|
534
|
+
If no container has room for the session, the call waits for additional capacity. It will block for
|
|
535
|
+
up to 25 minutes before giving up. To control the wait per call, use the HTTP API and apply your own retry
|
|
536
|
+
policy.
|
|
537
|
+
|
|
510
538
|
Args:
|
|
511
539
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
512
540
|
|
|
@@ -531,13 +559,10 @@ class _ServerSessionsManager:
|
|
|
531
559
|
self._validate()
|
|
532
560
|
assert url is not None, "Server has no URL."
|
|
533
561
|
|
|
534
|
-
headers = {
|
|
535
|
-
"Modal-Authorization": f"Bearer {await fn._get_flash_auth_token()}",
|
|
536
|
-
"x-modal-server-session-idle-timeout": str(idle_timeout),
|
|
537
|
-
}
|
|
538
|
-
|
|
539
562
|
try:
|
|
540
|
-
status, reason, body = await
|
|
563
|
+
status, reason, body = await _post_session_start(url, fn, idle_timeout)
|
|
564
|
+
except asyncio.TimeoutError:
|
|
565
|
+
raise asyncio.TimeoutError("Failed to start session: timed out") from None
|
|
541
566
|
except ServiceError as exc:
|
|
542
567
|
raise ExecutionError(f"Failed to start session: {exc}") from None
|
|
543
568
|
if status >= 400:
|
|
@@ -576,7 +601,7 @@ class _ServerSessionsManager:
|
|
|
576
601
|
"x-modal-server-session-token": token,
|
|
577
602
|
}
|
|
578
603
|
try:
|
|
579
|
-
status, reason, _body = await
|
|
604
|
+
status, reason, _body = await _post_session_terminate(url, headers)
|
|
580
605
|
except ServiceError as exc:
|
|
581
606
|
raise ExecutionError(f"Failed to terminate session: {exc}") from None
|
|
582
607
|
if status == 404:
|
|
@@ -272,9 +272,14 @@ class _Server:
|
|
|
272
272
|
...
|
|
273
273
|
|
|
274
274
|
async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int, str, str]:
|
|
275
|
-
"""POST to a sticky session control endpoint,
|
|
275
|
+
"""POST to a sticky session control endpoint, raising on 5xx."""
|
|
276
276
|
...
|
|
277
277
|
|
|
278
|
+
async def _post_session_start(
|
|
279
|
+
base_url: str, fn: modal._functions._Function, idle_timeout: int
|
|
280
|
+
) -> tuple[int, str, str]: ...
|
|
281
|
+
async def _post_session_terminate(base_url: str, headers: dict[str, str]) -> tuple[int, str, str]: ...
|
|
282
|
+
|
|
278
283
|
class _ServerSessionsManager:
|
|
279
284
|
"""mdmd:namespace"""
|
|
280
285
|
def __init__(self, server: _Server):
|
|
@@ -289,6 +294,10 @@ class _ServerSessionsManager:
|
|
|
289
294
|
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
290
295
|
for as long as it holds a live session.
|
|
291
296
|
|
|
297
|
+
If no container has room for the session, the call waits for additional capacity. It will block for
|
|
298
|
+
up to 25 minutes before giving up. To control the wait per call, use the HTTP API and apply your own retry
|
|
299
|
+
policy.
|
|
300
|
+
|
|
292
301
|
Args:
|
|
293
302
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
294
303
|
|
|
@@ -342,6 +351,10 @@ class ServerSessionsManager:
|
|
|
342
351
|
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
343
352
|
for as long as it holds a live session.
|
|
344
353
|
|
|
354
|
+
If no container has room for the session, the call waits for additional capacity. It will block for
|
|
355
|
+
up to 25 minutes before giving up. To control the wait per call, use the HTTP API and apply your own retry
|
|
356
|
+
policy.
|
|
357
|
+
|
|
345
358
|
Args:
|
|
346
359
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
347
360
|
|
|
@@ -367,6 +380,10 @@ class ServerSessionsManager:
|
|
|
367
380
|
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
368
381
|
for as long as it holds a live session.
|
|
369
382
|
|
|
383
|
+
If no container has room for the session, the call waits for additional capacity. It will block for
|
|
384
|
+
up to 25 minutes before giving up. To control the wait per call, use the HTTP API and apply your own retry
|
|
385
|
+
policy.
|
|
386
|
+
|
|
370
387
|
Args:
|
|
371
388
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
372
389
|
|
|
@@ -307,20 +307,19 @@ def create_channel_config(*, sustained_keepalive: bool = False) -> grpclib.confi
|
|
|
307
307
|
HTTP/2 keepalive probes keep idle transports warm through stateful
|
|
308
308
|
middleboxes and surface dead connections before retries reuse them.
|
|
309
309
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
open.
|
|
310
|
+
A probe goes out every `_keepalive_time` while the connection keeps sending
|
|
311
|
+
requests. `sustained_keepalive` also keeps probing a connection that only
|
|
312
|
+
awaits responses, for as long as it is open.
|
|
313
313
|
"""
|
|
314
|
-
ping_throttle_overrides: dict[str, Any] = {
|
|
314
|
+
ping_throttle_overrides: dict[str, Any] = {
|
|
315
|
+
# Default 300s floor between probes, applied regardless of data
|
|
316
|
+
# despite the name, so it has to stay under `_keepalive_time`.
|
|
317
|
+
"_http2_min_sent_ping_interval_without_data": GRPC_KEEPALIVE_TIME_SECS / 2,
|
|
318
|
+
}
|
|
315
319
|
if sustained_keepalive:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
"_http2_max_pings_without_data": 0,
|
|
320
|
-
# Default 300s floor between probes, applied regardless of data
|
|
321
|
-
# despite the name, so it has to stay under `_keepalive_time`.
|
|
322
|
-
"_http2_min_sent_ping_interval_without_data": GRPC_KEEPALIVE_TIME_SECS / 2,
|
|
323
|
-
}
|
|
320
|
+
# Default 2: probing stops after two probes with no request sent in
|
|
321
|
+
# between. Requests reset the count, acks don't. 0 = no cap.
|
|
322
|
+
ping_throttle_overrides["_http2_max_pings_without_data"] = 0
|
|
324
323
|
|
|
325
324
|
return grpclib.config.Configuration(
|
|
326
325
|
_keepalive_time=GRPC_KEEPALIVE_TIME_SECS,
|
|
@@ -112,6 +112,7 @@ def _time_range_stats_json(response: api_pb2.FunctionGetTimeRangeStatsResponse)
|
|
|
112
112
|
"input_percentile_stats": _distribution_map_json(
|
|
113
113
|
cast(dict[str, api_pb2.StatsPercentileDistribution], response.input_percentile_stats)
|
|
114
114
|
),
|
|
115
|
+
"container_total_count": response.container_total_count,
|
|
115
116
|
"container_started_count": response.container_started_count,
|
|
116
117
|
"container_error_count": response.container_error_count,
|
|
117
118
|
"container_creating_at_end_count": response.container_creating_at_end_count,
|
|
@@ -794,9 +795,10 @@ async def stats(
|
|
|
794
795
|
|
|
795
796
|
output.print("")
|
|
796
797
|
container_count = resp.container_started_count + resp.container_error_count
|
|
798
|
+
total_containers = resp.container_total_count
|
|
797
799
|
container_heading = Text("Containers", style=stats_style(STATS_SECTION_STYLE, use_color))
|
|
798
800
|
container_heading.append(" " * (46 - len("Containers")))
|
|
799
|
-
container_heading.append(f"{
|
|
801
|
+
container_heading.append(f"{total_containers:,} Total (")
|
|
800
802
|
container_heading.append(
|
|
801
803
|
f"{resp.container_creating_at_end_count:,} creating",
|
|
802
804
|
style=progress_style(resp.container_creating_at_end_count, use_color),
|
|
@@ -806,12 +808,12 @@ async def stats(
|
|
|
806
808
|
|
|
807
809
|
container_counts = Text(" ")
|
|
808
810
|
container_counts.append(
|
|
809
|
-
|
|
811
|
+
f"{resp.container_started_count:,} started",
|
|
810
812
|
style=success_style(resp.container_started_count, container_count, use_color),
|
|
811
813
|
)
|
|
812
814
|
container_counts.append(" · ")
|
|
813
815
|
container_counts.append(
|
|
814
|
-
|
|
816
|
+
f"{resp.container_error_count:,} errored",
|
|
815
817
|
style=problem_style(resp.container_error_count, container_count, use_color),
|
|
816
818
|
)
|
|
817
819
|
output.print(container_counts)
|
|
@@ -33,7 +33,6 @@ from ._stats import (
|
|
|
33
33
|
STATS_METADATA_STYLE,
|
|
34
34
|
STATS_PROBLEM_STYLE,
|
|
35
35
|
STATS_SECTION_STYLE,
|
|
36
|
-
_count_with_percentage,
|
|
37
36
|
_distribution_map_json,
|
|
38
37
|
_metric_rows,
|
|
39
38
|
_percentile_table,
|
|
@@ -560,6 +559,7 @@ def _stats_json(
|
|
|
560
559
|
"request_rate_per_second": history.request_rate_per_second,
|
|
561
560
|
"container_started_count": history.container_started_count,
|
|
562
561
|
"container_error_count": history.container_error_count,
|
|
562
|
+
"container_total_count": history.container_total_count,
|
|
563
563
|
"container_creating_at_end_count": history.container_creating_at_end_count,
|
|
564
564
|
"request_percentile_stats": _distribution_map_json(
|
|
565
565
|
cast(dict[str, api_pb2.StatsPercentileDistribution], history.request_percentile_stats)
|
|
@@ -754,9 +754,10 @@ async def _run_server_stats(
|
|
|
754
754
|
|
|
755
755
|
output.print("")
|
|
756
756
|
container_count = history.container_started_count + history.container_error_count
|
|
757
|
+
container_total_count = history.container_total_count
|
|
757
758
|
container_heading = Text("Containers", style=stats_style(STATS_SECTION_STYLE, use_color))
|
|
758
759
|
container_heading.append(" " * (46 - len("Containers")))
|
|
759
|
-
container_heading.append(f"{
|
|
760
|
+
container_heading.append(f"{container_total_count:,} Total (")
|
|
760
761
|
container_heading.append(
|
|
761
762
|
f"{history.container_creating_at_end_count:,} creating",
|
|
762
763
|
style=progress_style(history.container_creating_at_end_count, use_color),
|
|
@@ -766,12 +767,12 @@ async def _run_server_stats(
|
|
|
766
767
|
|
|
767
768
|
container_counts = Text(" ")
|
|
768
769
|
container_counts.append(
|
|
769
|
-
|
|
770
|
+
f"{history.container_started_count:,} started",
|
|
770
771
|
style=success_style(history.container_started_count, container_count, use_color),
|
|
771
772
|
)
|
|
772
773
|
container_counts.append(" · ")
|
|
773
774
|
container_counts.append(
|
|
774
|
-
|
|
775
|
+
f"{history.container_error_count:,} errored",
|
|
775
776
|
style=problem_style(history.container_error_count, container_count, use_color),
|
|
776
777
|
)
|
|
777
778
|
output.print(container_counts)
|
|
@@ -80,7 +80,7 @@ class _Client:
|
|
|
80
80
|
server_url: str,
|
|
81
81
|
client_type: int,
|
|
82
82
|
credentials: typing.Optional[tuple[str, str]],
|
|
83
|
-
version: str = "1.6.1.
|
|
83
|
+
version: str = "1.6.1.dev3",
|
|
84
84
|
*,
|
|
85
85
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
86
86
|
):
|
|
@@ -290,7 +290,7 @@ class Client:
|
|
|
290
290
|
server_url: str,
|
|
291
291
|
client_type: int,
|
|
292
292
|
credentials: typing.Optional[tuple[str, str]],
|
|
293
|
-
version: str = "1.6.1.
|
|
293
|
+
version: str = "1.6.1.dev3",
|
|
294
294
|
*,
|
|
295
295
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
296
296
|
):
|
|
@@ -134,13 +134,9 @@ def _agent_environment() -> str | None:
|
|
|
134
134
|
|
|
135
135
|
Returns the agent name if detected, or None otherwise.
|
|
136
136
|
|
|
137
|
-
Checks
|
|
138
|
-
|
|
137
|
+
Checks tool-specific environment variables used by known agent harnesses
|
|
138
|
+
first, then falls back to the emerging `AGENT` and `AI_AGENT` standards.
|
|
139
139
|
"""
|
|
140
|
-
# Emerging standard: AGENT=<name>
|
|
141
|
-
if agent := os.environ.get("AGENT"):
|
|
142
|
-
return agent
|
|
143
|
-
|
|
144
140
|
# Tool-specific environment variables
|
|
145
141
|
_TOOL_SPECIFIC_ENV_VARS: list[tuple[str, str]] = [
|
|
146
142
|
("CLAUDECODE", "claude-code"),
|
|
@@ -152,11 +148,23 @@ def _agent_environment() -> str | None:
|
|
|
152
148
|
("OPENCODE_CLIENT", "opencode"),
|
|
153
149
|
("GOOSE_TERMINAL", "goose"), # honk
|
|
154
150
|
("TRAE_AI_SHELL_ID", "trae"),
|
|
151
|
+
("PI_CODING_AGENT", "pi"),
|
|
152
|
+
("COPILOT_AGENT_SESSION_ID", "copilot"),
|
|
153
|
+
("ANTIGRAVITY_AGENT", "antigravity"),
|
|
154
|
+
("JUNIE_SHIM_PATH", "junie"),
|
|
155
155
|
]
|
|
156
156
|
for env_var, agent_name in _TOOL_SPECIFIC_ENV_VARS:
|
|
157
157
|
if os.environ.get(env_var):
|
|
158
158
|
return agent_name
|
|
159
159
|
|
|
160
|
+
# Emerging standards, checked last because neither value is normalized.
|
|
161
|
+
# `AGENT` may be a bare `1` rather than a name (opencode), and `AI_AGENT`
|
|
162
|
+
# may embed a version or suffix (e.g. `claude-code_2-1-268_agent`).
|
|
163
|
+
if agent := os.environ.get("AGENT"):
|
|
164
|
+
return agent
|
|
165
|
+
if agent := os.environ.get("AI_AGENT"):
|
|
166
|
+
return agent
|
|
167
|
+
|
|
160
168
|
return None
|
|
161
169
|
|
|
162
170
|
|
|
@@ -422,6 +430,9 @@ class Config:
|
|
|
422
430
|
|
|
423
431
|
Returns:
|
|
424
432
|
The transformed configuration value (type depends on the setting).
|
|
433
|
+
|
|
434
|
+
Raises:
|
|
435
|
+
KeyError: If ``key`` is not the name of a setting.
|
|
425
436
|
"""
|
|
426
437
|
if profile is None:
|
|
427
438
|
profile = _profile
|
|
@@ -472,6 +483,7 @@ class Config:
|
|
|
472
483
|
return repr(self.to_dict())
|
|
473
484
|
|
|
474
485
|
def to_dict(self, *, include_internal: bool = False):
|
|
486
|
+
"""Return every public setting along with its current value."""
|
|
475
487
|
return {
|
|
476
488
|
key: self.get(key) for key, setting in sorted(_SETTINGS.items()) if include_internal or not setting.internal
|
|
477
489
|
}
|
|
@@ -479,6 +491,16 @@ class Config:
|
|
|
479
491
|
|
|
480
492
|
config = Config()
|
|
481
493
|
|
|
494
|
+
__doc__config = """The process-wide `Config` instance the Modal client reads its settings from.
|
|
495
|
+
|
|
496
|
+
```python
|
|
497
|
+
from modal.config import config
|
|
498
|
+
|
|
499
|
+
log_level = config.get("loglevel")
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
"""
|
|
503
|
+
|
|
482
504
|
# Logging
|
|
483
505
|
|
|
484
506
|
logger = logging.getLogger("modal-client")
|
|
@@ -3121,6 +3121,7 @@ class _SidecarManager:
|
|
|
3121
3121
|
outbound_cidr_allowlist: Sequence[str] | None = None,
|
|
3122
3122
|
outbound_domain_allowlist: Sequence[str] | None = None,
|
|
3123
3123
|
include_oidc_identity_token: bool = False,
|
|
3124
|
+
proxy: _Proxy | None = None,
|
|
3124
3125
|
pty: bool = False,
|
|
3125
3126
|
experimental_memory_reserve_consume_mib: int | None = None,
|
|
3126
3127
|
) -> _SidecarContainer:
|
|
@@ -3153,6 +3154,8 @@ class _SidecarManager:
|
|
|
3153
3154
|
include_oidc_identity_token: If True, the sidecar receives a MODAL_IDENTITY_TOKEN env var for
|
|
3154
3155
|
OIDC-based auth (e.g. to AWS, GCP). The token identifies the sidecar container itself,
|
|
3155
3156
|
not the main container. Not supported for GPU Sandboxes.
|
|
3157
|
+
proxy: Reference to a Modal Proxy to use in front of this sidecar. Not supported for GPU
|
|
3158
|
+
Sandboxes.
|
|
3156
3159
|
pty: Whether to enable PTY for the sidecar container.
|
|
3157
3160
|
experimental_memory_reserve_consume_mib: Memory, in MiB, this sidecar consumes from the Sandbox's
|
|
3158
3161
|
sidecar memory reserve (the experimental `vm_sidecar_memory_reserve_mib` option).
|
|
@@ -3188,6 +3191,13 @@ class _SidecarManager:
|
|
|
3188
3191
|
"CloudBucketMount is not supported in sidecars when MODAL_USE_CONTROL_PLANE_SIDECAR_CREATE=0 is set; "
|
|
3189
3192
|
"unset it to use cloud bucket mounts."
|
|
3190
3193
|
)
|
|
3194
|
+
if proxy is not None and not self._sandbox._is_v2:
|
|
3195
|
+
raise InvalidError("Sandbox._experimental_sidecars.create(proxy=...) is not supported for GPU Sandboxes.")
|
|
3196
|
+
if proxy is not None and not via_control_plane:
|
|
3197
|
+
raise InvalidError(
|
|
3198
|
+
"Sandbox._experimental_sidecars.create(proxy=...) is not supported when "
|
|
3199
|
+
f"{_CONTROL_PLANE_SIDECAR_CREATE_ENV_VAR}=0 is set; unset it to use a proxy."
|
|
3200
|
+
)
|
|
3191
3201
|
|
|
3192
3202
|
if include_oidc_identity_token and not self._sandbox._is_v2:
|
|
3193
3203
|
raise InvalidError("include_oidc_identity_token is not supported for GPU Sandboxes.")
|
|
@@ -3224,12 +3234,18 @@ class _SidecarManager:
|
|
|
3224
3234
|
for _, mount in cloud_bucket_mounts
|
|
3225
3235
|
if mount.secret is not None and not mount.secret._is_ephemeral
|
|
3226
3236
|
]
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3237
|
+
resolver = Resolver()
|
|
3238
|
+
async with TaskContext() as tc:
|
|
3239
|
+
load_context = LoadContext(client=self._sandbox._client, task_context=tc)
|
|
3240
|
+
dependencies = [
|
|
3241
|
+
*resolvable_secrets,
|
|
3242
|
+
*(volume for _, volume in validated_volumes),
|
|
3243
|
+
*bucket_credential_secrets,
|
|
3244
|
+
*([proxy] if proxy is not None else []),
|
|
3245
|
+
]
|
|
3246
|
+
await asyncio.gather(
|
|
3247
|
+
*(resolver.load(dependency, load_context) for dependency in dependencies if not dependency._is_hydrated)
|
|
3248
|
+
)
|
|
3233
3249
|
|
|
3234
3250
|
# `env` takes precedence over environment variables from secrets
|
|
3235
3251
|
env_dict = _local_secret_env(secrets) | (env or {})
|
|
@@ -3257,6 +3273,7 @@ class _SidecarManager:
|
|
|
3257
3273
|
cloud_bucket_mounts=cloud_bucket_mount_protos,
|
|
3258
3274
|
network_access=network_access,
|
|
3259
3275
|
include_oidc_identity_token=include_oidc_identity_token,
|
|
3276
|
+
proxy_id=(proxy.object_id if proxy else None),
|
|
3260
3277
|
pty_info=pty_info,
|
|
3261
3278
|
resources=(
|
|
3262
3279
|
api_pb2.Resources(memory_mb=experimental_memory_reserve_consume_mib)
|
|
@@ -1298,6 +1298,7 @@ class _SidecarManager:
|
|
|
1298
1298
|
outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
|
|
1299
1299
|
outbound_domain_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
|
|
1300
1300
|
include_oidc_identity_token: bool = False,
|
|
1301
|
+
proxy: typing.Optional[modal.proxy._Proxy] = None,
|
|
1301
1302
|
pty: bool = False,
|
|
1302
1303
|
experimental_memory_reserve_consume_mib: typing.Optional[int] = None,
|
|
1303
1304
|
) -> _SidecarContainer:
|
|
@@ -1330,6 +1331,8 @@ class _SidecarManager:
|
|
|
1330
1331
|
include_oidc_identity_token: If True, the sidecar receives a MODAL_IDENTITY_TOKEN env var for
|
|
1331
1332
|
OIDC-based auth (e.g. to AWS, GCP). The token identifies the sidecar container itself,
|
|
1332
1333
|
not the main container. Not supported for GPU Sandboxes.
|
|
1334
|
+
proxy: Reference to a Modal Proxy to use in front of this sidecar. Not supported for GPU
|
|
1335
|
+
Sandboxes.
|
|
1333
1336
|
pty: Whether to enable PTY for the sidecar container.
|
|
1334
1337
|
experimental_memory_reserve_consume_mib: Memory, in MiB, this sidecar consumes from the Sandbox's
|
|
1335
1338
|
sidecar memory reserve (the experimental `vm_sidecar_memory_reserve_mib` option).
|
|
@@ -1782,6 +1785,7 @@ class SidecarManager:
|
|
|
1782
1785
|
outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
|
|
1783
1786
|
outbound_domain_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
|
|
1784
1787
|
include_oidc_identity_token: bool = False,
|
|
1788
|
+
proxy: typing.Optional[modal.proxy.Proxy] = None,
|
|
1785
1789
|
pty: bool = False,
|
|
1786
1790
|
experimental_memory_reserve_consume_mib: typing.Optional[int] = None,
|
|
1787
1791
|
) -> SidecarContainer:
|
|
@@ -1814,6 +1818,8 @@ class SidecarManager:
|
|
|
1814
1818
|
include_oidc_identity_token: If True, the sidecar receives a MODAL_IDENTITY_TOKEN env var for
|
|
1815
1819
|
OIDC-based auth (e.g. to AWS, GCP). The token identifies the sidecar container itself,
|
|
1816
1820
|
not the main container. Not supported for GPU Sandboxes.
|
|
1821
|
+
proxy: Reference to a Modal Proxy to use in front of this sidecar. Not supported for GPU
|
|
1822
|
+
Sandboxes.
|
|
1817
1823
|
pty: Whether to enable PTY for the sidecar container.
|
|
1818
1824
|
experimental_memory_reserve_consume_mib: Memory, in MiB, this sidecar consumes from the Sandbox's
|
|
1819
1825
|
sidecar memory reserve (the experimental `vm_sidecar_memory_reserve_mib` option).
|
|
@@ -1843,6 +1849,7 @@ class SidecarManager:
|
|
|
1843
1849
|
outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
|
|
1844
1850
|
outbound_domain_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
|
|
1845
1851
|
include_oidc_identity_token: bool = False,
|
|
1852
|
+
proxy: typing.Optional[modal.proxy.Proxy] = None,
|
|
1846
1853
|
pty: bool = False,
|
|
1847
1854
|
experimental_memory_reserve_consume_mib: typing.Optional[int] = None,
|
|
1848
1855
|
) -> SidecarContainer:
|
|
@@ -1875,6 +1882,8 @@ class SidecarManager:
|
|
|
1875
1882
|
include_oidc_identity_token: If True, the sidecar receives a MODAL_IDENTITY_TOKEN env var for
|
|
1876
1883
|
OIDC-based auth (e.g. to AWS, GCP). The token identifies the sidecar container itself,
|
|
1877
1884
|
not the main container. Not supported for GPU Sandboxes.
|
|
1885
|
+
proxy: Reference to a Modal Proxy to use in front of this sidecar. Not supported for GPU
|
|
1886
|
+
Sandboxes.
|
|
1878
1887
|
pty: Whether to enable PTY for the sidecar container.
|
|
1879
1888
|
experimental_memory_reserve_consume_mib: Memory, in MiB, this sidecar consumes from the Sandbox's
|
|
1880
1889
|
sidecar memory reserve (the experimental `vm_sidecar_memory_reserve_mib` option).
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: modal
|
|
3
3
|
description: >
|
|
4
|
-
This skill provides guidance for working with the Modal cloud platform.
|
|
5
|
-
this skill
|
|
6
|
-
`modal` SDK
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
This skill provides guidance for working with the Modal cloud platform. Read
|
|
5
|
+
this skill before answering any questions about Modal, reading any Modal docs,
|
|
6
|
+
inspecting the `modal` SDK, or interacting with any code that imports the
|
|
7
|
+
`modal` SDK in Python, Go, or JavaScript, even for simple tasks you already
|
|
8
|
+
know how to do. This skill should also trigger when the user needs to run
|
|
9
|
+
Python code with vertical or horizontal scalability (e.g. batch jobs), needs
|
|
10
|
+
access to GPUs (e.g. AI workloads including training and inference) or needs
|
|
11
|
+
to run untrusted processes in a sandbox, since Modal serves these use cases
|
|
12
|
+
well.
|
|
11
13
|
---
|
|
12
14
|
|
|
13
15
|
# Overview
|
|
@@ -16,21 +18,18 @@ This is a foundational skill for working with the Modal cloud platform.
|
|
|
16
18
|
|
|
17
19
|
Modal is a platform for AI workloads. It offers highly scalable serverless compute (including GPUs) with minimal configuration.
|
|
18
20
|
|
|
21
|
+
Using the CLI or running code that depends on the `modal` library requires internet access and an authorization token. There is no "local development mode" with Modal.
|
|
22
|
+
|
|
19
23
|
# Documentation
|
|
20
24
|
|
|
21
25
|
The official docs are the best source for up-to-date information on the platform. Make use of them when planning, debugging, or answering questions!
|
|
22
26
|
|
|
23
|
-
Modal's documentation is outlined at https://modal.com/llms.txt. This file contains titles and URLs for all public docs; reading it directly will guide you to specific content.
|
|
24
|
-
|
|
25
|
-
The docs are divided into three sections:
|
|
27
|
+
Modal's latest documentation, which may cover features introduced in newer SDK versions, is outlined at https://modal.com/llms.txt. This file contains titles and URLs for all public docs; reading it directly will guide you to specific content. Fetch the docs using a `.md` extension to get plain text.
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
- _API Reference_ pages have detailed information about each component of the SDK
|
|
29
|
-
- _Examples_ pages contain didactic examples of various AI applications on Modal
|
|
29
|
+
When installed via the Modal CLI, this skill installs local documentation in `references/` unless `--no-docs` is passed:
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
If this skill has been installed via the `modal` CLI, the docs are also bundled locally under `references/`.
|
|
31
|
+
- `api/`: An API reference generated locally from the signatures and docstrings of the SDK used to install or update the skill.
|
|
32
|
+
- `guide/` and `examples/`: Guides and examples downloaded from the online docs. These reflect the website at download time and may cover features unavailable in that SDK version.
|
|
34
33
|
|
|
35
34
|
You can also refer to https://modal.com/llms-full.txt, which aggregates all docs in a single very large file. Do *not* read this into your main context, but it may be useful for searching.
|
|
36
35
|
|
|
@@ -46,6 +45,8 @@ Tip: many CLI commands accept a `--json` flag to make their output more easily p
|
|
|
46
45
|
|
|
47
46
|
The `modal` CLI is part of the Python SDK and executes in a Python runtime. Depending on the user's preferred Python development workflow, you may need to prefix `modal` CLI invocations with, e.g., `uv run` to ensure a consistent Python environment. The CLI can also be used via `uvx` as a standalone tool, but only do this when working outside of a Python project.
|
|
48
47
|
|
|
48
|
+
Do not run Modal CLI commands with an attached terminal (TTY/PTY), so logs appear as plain text rather than spinner redraws.
|
|
49
|
+
|
|
49
50
|
# Getting up to date
|
|
50
51
|
|
|
51
52
|
You have significant knowledge about Modal from your training data but may not be aware of new features or recent changes to the API. Modal is continuously adding new features. Reading relevant docs while planning or debugging can help you discover the most up-to-date way to accomplish a task on Modal.
|
|
@@ -56,19 +57,13 @@ The Modal CLI provides a `modal changelog` command for learning about recent cha
|
|
|
56
57
|
- `modal changelog --since [VERSION]` when migrating a codebase to a newer version
|
|
57
58
|
- `modal changelog --newer` to discover features that would be available on update
|
|
58
59
|
|
|
59
|
-
Note: `modal changelog`
|
|
60
|
+
Note: the output of `modal changelog` may be long. Do not load it fully into your context.
|
|
60
61
|
|
|
61
62
|
Run `modal --version` to see the SDK version that is in use.
|
|
62
63
|
|
|
63
64
|
If your code exercises deprecated APIs, Modal will issue warnings. Use the warning message and related documentation to migrate your code to stable API.
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# Auth
|
|
68
|
-
|
|
69
|
-
Modal is a cloud platform. Using the CLI or running code that depends on the `modal` library requires internet access and an authorization token. There is no "local development mode" with Modal.
|
|
70
|
-
|
|
71
|
-
You can use the `modal token` CLI to create a new token (note: this workflow requires human user involvement) or to debug authorization issues. Token setup only needs to happen once, so assume it is configured unless you encounter issues.
|
|
66
|
+
Before using this skill, check that its version in the metadata is identical to `modal --version`. If not, run `modal skills update` to update it, passing `--claude` if this skill lives under a `.claude/` directory and `--global` if it lives under the home directory rather than the project.
|
|
72
67
|
|
|
73
68
|
# Async Python
|
|
74
69
|
|
|
@@ -181,6 +181,7 @@ class FunctionStats:
|
|
|
181
181
|
container_error_count: int
|
|
182
182
|
container_creating_at_end_count: int
|
|
183
183
|
container_percentile_stats: dict[str, StatsPercentileDistribution]
|
|
184
|
+
container_total_count: int
|
|
184
185
|
variant_count: int
|
|
185
186
|
all_variants: bool
|
|
186
187
|
|
|
@@ -198,6 +199,7 @@ class FunctionStats:
|
|
|
198
199
|
},
|
|
199
200
|
container_started_count=proto.container_started_count,
|
|
200
201
|
container_error_count=proto.container_error_count,
|
|
202
|
+
container_total_count=proto.container_total_count,
|
|
201
203
|
container_creating_at_end_count=proto.container_creating_at_end_count,
|
|
202
204
|
container_percentile_stats={
|
|
203
205
|
k: StatsPercentileDistribution._from_proto(v) for k, v in proto.container_percentile_stats.items()
|
|
@@ -256,6 +258,7 @@ class ServerStats:
|
|
|
256
258
|
container_started_count: int
|
|
257
259
|
container_error_count: int
|
|
258
260
|
container_creating_at_end_count: int
|
|
261
|
+
container_total_count: int
|
|
259
262
|
container_percentile_stats: dict[str, StatsPercentileDistribution]
|
|
260
263
|
inference: InferenceStats | None
|
|
261
264
|
|
|
@@ -274,6 +277,7 @@ class ServerStats:
|
|
|
274
277
|
container_started_count=proto.container_started_count,
|
|
275
278
|
container_error_count=proto.container_error_count,
|
|
276
279
|
container_creating_at_end_count=proto.container_creating_at_end_count,
|
|
280
|
+
container_total_count=proto.container_total_count,
|
|
277
281
|
container_percentile_stats={
|
|
278
282
|
name: StatsPercentileDistribution._from_proto(distribution)
|
|
279
283
|
for name, distribution in proto.container_percentile_stats.items()
|
|
@@ -888,6 +888,8 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
888
888
|
async def commit(self):
|
|
889
889
|
"""Commit changes to a mounted volume.
|
|
890
890
|
|
|
891
|
+
Must be called inside a container that mounts the Volume.
|
|
892
|
+
|
|
891
893
|
If successful, the changes made are now persisted in durable storage and available to other containers accessing
|
|
892
894
|
the volume.
|
|
893
895
|
"""
|
|
@@ -906,6 +908,8 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
906
908
|
async def reload(self):
|
|
907
909
|
"""Make latest committed state of volume available in the running container.
|
|
908
910
|
|
|
911
|
+
Must be called inside a container that mounts the Volume.
|
|
912
|
+
|
|
909
913
|
Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
|
|
910
914
|
reloading.
|
|
911
915
|
|