runpod 2.1.0__tar.gz → 2.2.0__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.
- {runpod-2.1.0 → runpod-2.2.0}/CHANGELOG.md +27 -0
- {runpod-2.1.0/runpod.egg-info → runpod-2.2.0}/PKG-INFO +13 -2
- {runpod-2.1.0 → runpod-2.2.0}/README.md +12 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/references/command_line_interface.md +40 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/start_here.md +2 -0
- runpod-2.2.0/docs/getting_started.md +34 -0
- {runpod-2.1.0 → runpod-2.2.0}/requirements.txt +0 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/__init__.py +15 -2
- runpod-2.2.0/runpod/_hosts.py +54 -0
- runpod-2.2.0/runpod/_serialization.py +21 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_version.py +3 -3
- runpod-2.2.0/runpod/_volume_transfer_utils.py +171 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/graphql.py +2 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/mutations/apps.py +6 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/__init__.py +2 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/api.py +42 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/app.py +11 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/auth.py +2 -3
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/build.py +0 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/context.py +22 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/datacenter.py +16 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/deploy.py +127 -31
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/handles.py +27 -5
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/images.py +3 -5
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/placement.py +8 -4
- runpod-2.2.0/runpod/apps/protocol.py +86 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/serialization.py +28 -35
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/spec.py +32 -4
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/stubs.py +31 -2
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/targets.py +121 -59
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/tasks.py +130 -82
- runpod-2.2.0/runpod/apps/utils/network.py +65 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/volume.py +96 -2
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/config/functions.py +46 -18
- runpod-2.2.0/runpod/network_volume_transfer.py +981 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/console.py +9 -9
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/endpoint.py +2 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/main.py +19 -7
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/pod.py +2 -1
- runpod-2.2.0/runpod/rp_cli/volume.py +610 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_fastapi.py +13 -4
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_http.py +15 -2
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_job.py +10 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_scale.py +7 -2
- {runpod-2.1.0 → runpod-2.2.0/runpod.egg-info}/PKG-INFO +13 -2
- {runpod-2.1.0 → runpod-2.2.0}/runpod.egg-info/SOURCES.txt +15 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod.egg-info/requires.txt +0 -1
- {runpod-2.1.0 → runpod-2.2.0}/runpod.egg-info/scm_file_list.json +15 -1
- runpod-2.2.0/runpod.egg-info/scm_version.json +8 -0
- runpod-2.2.0/tests/e2e/test_network_volume_transfer_live.py +74 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_auth.py +5 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_config_params.py +54 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_deploy.py +35 -4
- runpod-2.2.0/tests/test_apps/test_deployed_tasks.py +471 -0
- runpod-2.2.0/tests/test_apps/test_deployment_validation.py +345 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_dispatch.py +25 -9
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_placement.py +44 -0
- runpod-2.2.0/tests/test_apps/test_runtime_call_validation.py +172 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_tasks.py +15 -13
- runpod-2.2.0/tests/test_apps/test_transport_parity.py +170 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_volume.py +16 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_console.py +7 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_deploy_command.py +28 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_endpoint_command.py +18 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_management_commands.py +29 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_pod_command.py +8 -0
- runpod-2.2.0/tests/test_hosts.py +67 -0
- runpod-2.2.0/tests/test_network_volume_transfer.py +2004 -0
- runpod-2.2.0/tests/test_profile.py +79 -0
- runpod-2.2.0/tests/test_serialization.py +231 -0
- runpod-2.2.0/tests/test_serverless/test_refresh_worker.py +640 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_rp_volume_cache.py +3 -0
- runpod-2.1.0/docs/getting_Started.md +0 -21
- runpod-2.1.0/runpod/apps/protocol.py +0 -60
- runpod-2.1.0/runpod/apps/utils/network.py +0 -33
- runpod-2.1.0/runpod/rp_cli/volume.py +0 -298
- runpod-2.1.0/runpod.egg-info/scm_version.json +0 -8
- {runpod-2.1.0 → runpod-2.2.0}/.dockerignore +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/dependabot.yml +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/tape_collection/cli_config.tape +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/tape_collection/cli_help.tape +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/tape_collection/cli_ssh.tape +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/tests.json +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/workflows/cd.yml +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/workflows/ci.yml +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.github/workflows/vhs.yml +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/.gitignore +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/ARCHITECTURE.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/CONTRIBUTING.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/LICENSE +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/MANIFEST.in +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/Makefile +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/SECURITY.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/build_tools/compile_gpu_test.sh +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/build_tools/gpu_test.c +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/api/handling_errors.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/api/queries.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/demos/config.gif +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/demos/help.gif +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/demos/ssh.gif +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/references/projects.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/cli/references/runpod.toml.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/images/env_var_location.png +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/functions/error_handling.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/functions/logging.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/gpu_binary_compilation.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/local_testing.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/utils/rp_cleanup.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/utils/rp_debugger.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/utils/rp_download.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/utils/rp_upload.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/utils/rp_validator.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/volume_cache.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/worker.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/worker_fitness_checks.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/docs/serverless/worker_realtime.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/api/create_container_registry_auth.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/api/create_endpoint.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/api/create_template.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/api/get_endpoints.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/apps/README.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/apps/gpu_inference.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/apps/hello_world.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/apps/streaming.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/apps/train_and_eval.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/apps/web_service.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/endpoints/asyncio_job_request.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/endpoints/health.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/endpoints/run.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/endpoints/run_sync.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/endpoints/streaming.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/rest_wrapper.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/serverless/concurrent_handler.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/serverless/logger.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/serverless/simple_handler.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/examples/serverless/volume_cache_handler.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/pyproject.toml +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/pytest.ini +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_binary_helpers.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_health/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_health/cuda.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_health/fitness.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_health/gpu.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_health/system.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_logger.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/_startup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/agent.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/_rate_limit.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/ctl_commands.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/mutations/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/mutations/container_register_auth.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/queries/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/queries/apps.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/queries/user.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/api/rest.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/dev.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/discovery.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/discovery_state.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/entrypoint.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/errors.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/gpu.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/init.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/invoker.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/job.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/logs.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/manage.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/markers.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/model.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/monitor.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/registry.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/schedule.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/secret.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/shim.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/utils/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/utils/client.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/utils/events.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/utils/lookup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/utils/names.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/apps/watch.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/entry.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/config/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/pod/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/pod/commands.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/ssh/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/ssh/commands.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/groups/ssh/functions.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/utils/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/utils/rp_info.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/utils/rp_runpodignore.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/utils/rp_userspace.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/cli/utils/ssh_cmd.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/endpoint/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/endpoint/asyncio/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/endpoint/asyncio/asyncio_runner.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/endpoint/helpers.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/endpoint/runner.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/error.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/http_client.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/api.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/catalog.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/compute.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/endpoint_config.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/logs.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/options.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/output.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/rp_cli/update.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/binaries/README.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/binaries/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/binaries/gpu_test +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_capture.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_fitness.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_gpu_fitness.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_handler.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_local.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_logger.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_ping.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_prestart.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_progress.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_system_fitness.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/rp_tips.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/modules/worker_state.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_cleanup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_cuda.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_debugger.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_download.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_model_cache.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_ssrf.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_upload.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_validator.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/utils/rp_volume_cache.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/serverless/worker.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/user_agent.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod/version.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod.egg-info/dependency_links.txt +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod.egg-info/entry_points.txt +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/runpod.egg-info/top_level.txt +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/scripts/README.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/scripts/benchmark_cold_start.sh +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/scripts/compare_benchmarks.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/setup.cfg +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/setup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/apps_live_smoke.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/conftest.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/e2e_provisioner.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/01_hello_world.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/02_invocation_styles.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/03_gpu.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/04_dependencies.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/05_api_service.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/06_tasks.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/07_pipelines.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/08_volumes.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/09_secrets.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/10_cached_models.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/11_train_eval.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/12_custom_image.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/13_streaming.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/README.md +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/examples/run_all.sh +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/fixtures/cold_start/handler.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/fixtures/cold_start/pyproject.toml +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/matrix/main.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/matrix/run.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/task_pod_smoke.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/test_cold_start.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/test_mock_worker.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/e2e/tests.json +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/fixtures/mock_gpu_test +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_agent.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_api/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_api/test_ctl_commands.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_api/test_rate_limit.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_api/test_rest.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/conftest.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_agent_auth.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_api_client.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_app.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_build.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_compat_lock.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_dev.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_discovery.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_gpu_catalog.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_images.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_job.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_logs.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_manage.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_markers.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_model.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_monitor.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_registry.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_retry.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_secret.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_shim.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_targets.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_apps/test_watch.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_agent_login.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_catalog_command.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_api.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_groups/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_groups/test_config_functions.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_groups/test_pod_commands.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_groups/test_ssh_commands.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_groups/test_ssh_dispatch.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_groups/test_ssh_functions.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_logs.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_options.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_output.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_utils/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_utils/test_info.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_utils/test_runpodignore.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_utils/test_ssh_cmd.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_cli_utils/test_userspace.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_compute.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_init_command.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_update_command.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_cli/test_volume_command.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_endpoint/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_endpoint/test_asyncio_init.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_endpoint/test_asyncio_runner.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_endpoint/test_init.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_endpoint/test_runner.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_error.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_init.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_performance/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_performance/test_cold_start.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/local_sim/.env_example +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/local_sim/Makefile +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/local_sim/localhost.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/local_sim/worker.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_capture.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_init.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_integration_worker_state.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fastapi.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/conftest.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_force_kill.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_gpu_checks.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_gpu_integration.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_registration.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_safe_startup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_startup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_fitness/test_system_checks.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_handler.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_http.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_job.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_local.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_logger.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_ping.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_progress.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_scale.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_state.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_modules/test_tips.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_prestart.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_prestart_lifecycle.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_rp_scale.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_scale_mirror.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_cleanup.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_cuda.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_debugger.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_download.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_model_cache.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_ssrf.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_upload.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils/test_validate.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_utils_init.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_serverless/test_worker.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_shared/__init__.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_shared/test_auth.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_user_agent.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_version.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/test_whatever.py +0 -0
- {runpod-2.1.0 → runpod-2.2.0}/tests/whatever.py +0 -0
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
### Features
|
|
6
6
|
|
|
7
7
|
* Apps expose `NetworkVolume` and `GlobalVolume` resources with explicit path-keyed mounts, lazy create-if-missing provisioning, and execution-bound filesystem access. Global volumes use GraphQL for lookup and creation.
|
|
8
|
+
* Deployed Flash calls use endpoint or pod runtime credentials. Release requires deployed support from [RunPod#6686](https://github.com/runpod/RunPod/pull/6686), [ai-api#1176](https://github.com/runpod/ai-api/pull/1176), and [sdk-runtime#4](https://github.com/runpod-workers/sdk-runtime/pull/4).
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
|
|
12
|
+
* Production endpoint environment variables reserve `RUNPOD_API_KEY` and runtime identity fields. Workers authenticate Flash calls with platform-issued runtime credentials; application credentials must use non-reserved environment names.
|
|
13
|
+
* Deployed workers can invoke Flash resources only within their own app and environment, including through `Queue`, `Api`, and `Task` stubs. Local clients use account credentials subject to their permissions.
|
|
14
|
+
* Remote queues, tasks, and JSON API calls require finite JSON-compatible arguments and results in local, dev, and deployed execution. Dev workers receive source separately from the JSON argument contract. Explicit `.local()` calls run Python directly.
|
|
8
15
|
|
|
9
16
|
### Bug Fixes
|
|
10
17
|
|
|
@@ -15,6 +22,26 @@
|
|
|
15
22
|
* Deployment artifacts apply project ignore rules and mandatory secret-file exclusions while preserving vendored dependency assets.
|
|
16
23
|
* Synchronous calls wait across polling timeouts on Python 3.10 and propagate operation timeouts.
|
|
17
24
|
|
|
25
|
+
## [2.2.0](https://github.com/runpod/runpod-python/compare/v2.1.0...v2.2.0) (2026-11-09)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* invoke deployed flash tasks with runtime credentials (SEC-375 CON-1833) ([#644](https://github.com/runpod/runpod-python/issues/644)) ([367f174](https://github.com/runpod/runpod-python/commit/367f174ef14cc7fcf909a3b26413c3f2d38cbd9b))
|
|
31
|
+
* make console, proxy, and endpoint hosts configurable ([#684](https://github.com/runpod/runpod-python/issues/684)) ([b9583f7](https://github.com/runpod/runpod-python/commit/b9583f7c0a02e6c0cbb9598538393fc239d88852))
|
|
32
|
+
* resumable network volume upload and download ([#655](https://github.com/runpod/runpod-python/issues/655)) ([a677ee9](https://github.com/runpod/runpod-python/commit/a677ee9f08f21da5a29cc126ed63032fc1063365))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Bug Fixes
|
|
36
|
+
|
|
37
|
+
* **apps:** accept datacenter ids outside the built-in list ([#690](https://github.com/runpod/runpod-python/issues/690)) ([dbc251a](https://github.com/runpod/runpod-python/commit/dbc251ac8ba87ed4cc53f71010bc8c87dba1c77d))
|
|
38
|
+
* **serverless:** stop taking jobs after a per-job refresh_worker (SLS-900) ([#663](https://github.com/runpod/runpod-python/issues/663)) ([8ca9d9b](https://github.com/runpod/runpod-python/commit/8ca9d9bcfdc0ad1792e899cdef49a4fd172fbb13))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
|
|
43
|
+
* rename getting_Started.md to getting_started.md ([#697](https://github.com/runpod/runpod-python/issues/697)) ([fbcabcd](https://github.com/runpod/runpod-python/commit/fbcabcd73889783d8e03b6261154891f13b0d9d6))
|
|
44
|
+
|
|
18
45
|
## [2.1.0](https://github.com/runpod/runpod-python/compare/v2.0.0...v2.1.0) (2026-11-03)
|
|
19
46
|
|
|
20
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runpod
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: 🐍 | Python library for Runpod API and serverless worker SDK.
|
|
5
5
|
Home-page: https://runpod.io
|
|
6
6
|
Author: Runpod
|
|
@@ -34,7 +34,6 @@ Requires-Dist: backoff>=2.2.1
|
|
|
34
34
|
Requires-Dist: boto3>=1.43.118
|
|
35
35
|
Requires-Dist: certifi>=2026.10.19
|
|
36
36
|
Requires-Dist: click>=8.5.0
|
|
37
|
-
Requires-Dist: cloudpickle>=3.1.2
|
|
38
37
|
Requires-Dist: colorama<0.4.7,>=0.4.6
|
|
39
38
|
Requires-Dist: cryptography>=50.0.2
|
|
40
39
|
Requires-Dist: fastapi[all]>=0.162.0
|
|
@@ -143,6 +142,18 @@ make setup
|
|
|
143
142
|
make test
|
|
144
143
|
```
|
|
145
144
|
|
|
145
|
+
## Remote values
|
|
146
|
+
|
|
147
|
+
Queue and task calls use finite JSON-compatible arguments, return values,
|
|
148
|
+
and stream chunks. JSON conversion turns tuples into lists and supported
|
|
149
|
+
dictionary keys into strings, including in same-worker remote calls.
|
|
150
|
+
Bytes, custom objects, circular references, and non-finite numbers produce
|
|
151
|
+
explicit errors. `.local()` remains a direct Python call.
|
|
152
|
+
|
|
153
|
+
Successful function responses contain `json_result`, including for a null
|
|
154
|
+
return value. SDK and worker runtimes share the JSON protocol validators;
|
|
155
|
+
use compatible package releases when building worker images.
|
|
156
|
+
|
|
146
157
|
## ⚡ | Serverless Worker (SDK)
|
|
147
158
|
|
|
148
159
|
This python package can also be used to create a serverless worker that can be deployed to Runpod as a custom endpoint API.
|
|
@@ -84,6 +84,18 @@ make setup
|
|
|
84
84
|
make test
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
## Remote values
|
|
88
|
+
|
|
89
|
+
Queue and task calls use finite JSON-compatible arguments, return values,
|
|
90
|
+
and stream chunks. JSON conversion turns tuples into lists and supported
|
|
91
|
+
dictionary keys into strings, including in same-worker remote calls.
|
|
92
|
+
Bytes, custom objects, circular references, and non-finite numbers produce
|
|
93
|
+
explicit errors. `.local()` remains a direct Python call.
|
|
94
|
+
|
|
95
|
+
Successful function responses contain `json_result`, including for a null
|
|
96
|
+
return value. SDK and worker runtimes share the JSON protocol validators;
|
|
97
|
+
use compatible package releases when building worker images.
|
|
98
|
+
|
|
87
99
|
## ⚡ | Serverless Worker (SDK)
|
|
88
100
|
|
|
89
101
|
This python package can also be used to create a serverless worker that can be deployed to Runpod as a custom endpoint API.
|
|
@@ -23,6 +23,10 @@ rp ssh POD_ID # open a terminal on a pod
|
|
|
23
23
|
rp pod list
|
|
24
24
|
rp pod create
|
|
25
25
|
rp pod connect POD_ID
|
|
26
|
+
|
|
27
|
+
# Network volume files
|
|
28
|
+
rp volume upload VOLUME ./dataset datasets/dataset
|
|
29
|
+
rp volume download VOLUME outputs/ ./outputs
|
|
26
30
|
```
|
|
27
31
|
|
|
28
32
|
## Overview
|
|
@@ -44,6 +48,7 @@ rp login --profile staging # save browser credentials in a named profile
|
|
|
44
48
|
|
|
45
49
|
Credentials are stored in `~/.runpod/config.toml`.
|
|
46
50
|
Use `--profile NAME` with either login method to update only that profile; the default is `default`.
|
|
51
|
+
Set `RUNPOD_PROFILE=NAME` to select the profile for all commands and for `runpod` in Python; `--profile` overrides it, and `RUNPOD_API_KEY` overrides any stored profile.
|
|
47
52
|
|
|
48
53
|
### Flash apps
|
|
49
54
|
|
|
@@ -62,3 +67,38 @@ entrypoint or a resource function; interrupt discovery if an import blocks.
|
|
|
62
67
|
On Ctrl-C, a dev session waits for its running local entrypoint to finish before
|
|
63
68
|
deleting endpoints. Blocking entrypoint code can delay shutdown. Cleanup failures
|
|
64
69
|
exit with an error and report the endpoint IDs that still need deletion.
|
|
70
|
+
|
|
71
|
+
### Network volume files
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
rp volume upload VOLUME ./model.safetensors models/
|
|
75
|
+
rp volume upload VOLUME ./dataset datasets/dataset
|
|
76
|
+
rp volume download VOLUME models/model.safetensors
|
|
77
|
+
rp volume download VOLUME outputs/ ./outputs
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Transfers use the volume's S3-compatible API, so they need S3 API keys
|
|
81
|
+
(separate from the API key): set `RUNPOD_S3_ACCESS_KEY` and
|
|
82
|
+
`RUNPOD_S3_SECRET_KEY`, pass `--s3-access-key` / `--s3-secret-key`, or save
|
|
83
|
+
`s3_access_key` / `s3_secret_key` in `~/.runpod/config.toml`. The CLI prompts
|
|
84
|
+
for missing keys and offers to save them. You can omit `VOLUME` in an
|
|
85
|
+
interactive session to pick a volume from a list. The
|
|
86
|
+
volume must be in a [datacenter with the S3 API](https://docs.runpod.io/storage/s3-api).
|
|
87
|
+
`VOLUME` is the volume's ID or name, looked up with your API key.
|
|
88
|
+
|
|
89
|
+
To resume an interrupted transfer, run the same command again. Files that
|
|
90
|
+
are unchanged (same size, destination not older) are skipped, a large upload
|
|
91
|
+
reuses the parts it already sent, and a large download keeps its finished
|
|
92
|
+
chunks in `<name>.rpdownload`. `--force` transfers every file. Parts of an
|
|
93
|
+
upload that is never resumed stay in the volume's hidden `.s3compat_uploads`
|
|
94
|
+
folder and count toward its size.
|
|
95
|
+
|
|
96
|
+
The same transfers are available from Python:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from runpod import NetworkVolume
|
|
100
|
+
|
|
101
|
+
volume = NetworkVolume("my-volume")
|
|
102
|
+
volume.upload("./dataset", "datasets/dataset")
|
|
103
|
+
volume.download("outputs/", "./outputs")
|
|
104
|
+
```
|
|
@@ -12,6 +12,8 @@ Store your Runpod API key by running `rp login` (approve in the browser, then pa
|
|
|
12
12
|
|
|
13
13
|
Pass `--profile staging` with either login method to update a named credential profile without changing the others. The default profile is `default`.
|
|
14
14
|
|
|
15
|
+
Set `RUNPOD_PROFILE=staging` to use that profile for every command, including `rp login`. `--profile` takes precedence over `RUNPOD_PROFILE`, and `RUNPOD_API_KEY` takes precedence over any stored profile.
|
|
16
|
+
|
|
15
17
|
### SSH
|
|
16
18
|
|
|
17
19
|
Add an SSH key to your account by running `rp ssh add`. To use an existing key pass `--key` or `--key-file`. Keys are stored in `~/.runpod/ssh/`. If no key is specified a new one is generated and stored.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
The Runpod python library is a powerful library providing SDK functions, API access, and CLI commands for interacting with the Runpod platform.
|
|
4
|
+
|
|
5
|
+
## Credentials File
|
|
6
|
+
|
|
7
|
+
`rp login` stores your API key in `~/.runpod/config.toml`, one table per profile:
|
|
8
|
+
|
|
9
|
+
```toml
|
|
10
|
+
[default]
|
|
11
|
+
api_key = "YOUR_RUNPOD_API_KEY"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Profile
|
|
15
|
+
|
|
16
|
+
By default, credentials are stored under and read from the `default` profile. To store a key under another profile, pass `--profile` to `rp login`:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
rp login --profile my_profile
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
To use that profile, set the `RUNPOD_PROFILE` environment variable before importing `runpod`. The library reads the profile and its API key once, at import time.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export RUNPOD_PROFILE=my_profile
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`RUNPOD_API_KEY`, when set, takes precedence over any stored profile. Inside a script, you can also set the key directly:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import runpod
|
|
32
|
+
|
|
33
|
+
runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
34
|
+
```
|
|
@@ -54,6 +54,7 @@ _LAZY_ATTRS = {
|
|
|
54
54
|
"Model": "runpod.apps",
|
|
55
55
|
"NetworkVolume": "runpod.apps",
|
|
56
56
|
"Queue": "runpod.apps",
|
|
57
|
+
"Task": "runpod.apps",
|
|
57
58
|
"Secret": "runpod.apps",
|
|
58
59
|
"Volume": "runpod.apps",
|
|
59
60
|
"is_local": "runpod.apps",
|
|
@@ -86,6 +87,18 @@ __all__ = [
|
|
|
86
87
|
]
|
|
87
88
|
|
|
88
89
|
|
|
90
|
+
DEFAULT_PROFILE = "default"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _selected_profile() -> str:
|
|
94
|
+
"""credential profile in ~/.runpod/config.toml to use.
|
|
95
|
+
|
|
96
|
+
set RUNPOD_PROFILE to pick a named profile (saved with
|
|
97
|
+
`rp login --profile NAME`); unset or blank means "default".
|
|
98
|
+
"""
|
|
99
|
+
return os.environ.get("RUNPOD_PROFILE", "").strip() or DEFAULT_PROFILE
|
|
100
|
+
|
|
101
|
+
|
|
89
102
|
def _load_api_key():
|
|
90
103
|
"""resolve the api key from the environment, then stored credentials.
|
|
91
104
|
|
|
@@ -102,7 +115,7 @@ def _load_api_key():
|
|
|
102
115
|
).get_credentials(profile)
|
|
103
116
|
except Exception: # noqa: BLE001 - no stored key is a valid state
|
|
104
117
|
return None
|
|
105
|
-
return creds
|
|
118
|
+
return creds.get("api_key") if creds else None
|
|
106
119
|
|
|
107
120
|
|
|
108
121
|
def __getattr__(name):
|
|
@@ -118,7 +131,7 @@ def __getattr__(name):
|
|
|
118
131
|
# ------------------------------- Config Paths ------------------------------- #
|
|
119
132
|
SSH_KEY_PATH = os.path.expanduser("~/.runpod/ssh")
|
|
120
133
|
|
|
121
|
-
profile =
|
|
134
|
+
profile = _selected_profile() # pylint: disable=invalid-name
|
|
122
135
|
|
|
123
136
|
api_key = _load_api_key() # pylint: disable=invalid-name
|
|
124
137
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Service hosts used to build console, proxy, and endpoint URLs.
|
|
2
|
+
|
|
3
|
+
Each host can be overridden with an environment variable, which is read on
|
|
4
|
+
every call so a change takes effect without re-importing ``runpod``. Unset or
|
|
5
|
+
blank values fall back to the public Runpod hosts.
|
|
6
|
+
|
|
7
|
+
RUNPOD_CONSOLE_URL web console base URL (default https://console.runpod.io)
|
|
8
|
+
RUNPOD_PROXY_DOMAIN pod HTTP proxy domain (default proxy.runpod.net)
|
|
9
|
+
RUNPOD_ENDPOINT_BASE_URL serverless base URL (default https://api.runpod.ai/v2)
|
|
10
|
+
|
|
11
|
+
The endpoint domain is the host of ``RUNPOD_ENDPOINT_BASE_URL``. When that is
|
|
12
|
+
unset or blank, a non-blank ``runpod.endpoint_url_base`` set programmatically
|
|
13
|
+
is used instead.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> proxy_url("abc123", 8080)
|
|
17
|
+
'https://abc123-8080.proxy.runpod.net'
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from typing import Union
|
|
22
|
+
|
|
23
|
+
DEFAULT_CONSOLE_URL = "https://console.runpod.io"
|
|
24
|
+
DEFAULT_PROXY_DOMAIN = "proxy.runpod.net"
|
|
25
|
+
DEFAULT_ENDPOINT_BASE_URL = "https://api.runpod.ai/v2"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _env(name: str, default: str) -> str:
|
|
29
|
+
value = os.environ.get(name, "").strip()
|
|
30
|
+
return value or default
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def console_url() -> str:
|
|
34
|
+
"""Web console base URL, without a trailing slash."""
|
|
35
|
+
return _env("RUNPOD_CONSOLE_URL", DEFAULT_CONSOLE_URL).rstrip("/")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def proxy_url(pod_id: str, port: Union[int, str]) -> str:
|
|
39
|
+
"""Public HTTP proxy URL for ``port`` on ``pod_id``."""
|
|
40
|
+
domain = _env("RUNPOD_PROXY_DOMAIN", DEFAULT_PROXY_DOMAIN).strip("/")
|
|
41
|
+
return f"https://{pod_id}-{port}.{domain}"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def endpoint_domain() -> str:
|
|
45
|
+
"""Host of the serverless data plane, e.g. ``api.runpod.ai``.
|
|
46
|
+
|
|
47
|
+
Load-balanced endpoints are served on ``<endpoint_id>.<endpoint_domain>``.
|
|
48
|
+
"""
|
|
49
|
+
import runpod
|
|
50
|
+
|
|
51
|
+
module_base = (runpod.endpoint_url_base or "").strip()
|
|
52
|
+
base = _env("RUNPOD_ENDPOINT_BASE_URL", module_base or DEFAULT_ENDPOINT_BASE_URL)
|
|
53
|
+
host = base.split("://", 1)[-1]
|
|
54
|
+
return host.split("/", 1)[0]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""finite json validation and wire-value conversion for callers and workers."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _json_text(value: Any, label: str) -> str:
|
|
8
|
+
try:
|
|
9
|
+
return json.dumps(value, allow_nan=False)
|
|
10
|
+
except (TypeError, ValueError, RecursionError) as exc:
|
|
11
|
+
raise TypeError(f"{label} must be json-serializable: {exc}") from exc
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_json(value: Any, label: str = "value") -> None:
|
|
15
|
+
"""require values encodable as finite json."""
|
|
16
|
+
_json_text(value, label)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def normalize_json(value: Any, label: str = "value") -> Any:
|
|
20
|
+
"""apply json's container and key conversions without a network roundtrip."""
|
|
21
|
+
return json.loads(_json_text(value, label))
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.2.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g89d72b8c4'
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Filesystem and checksum helpers for resumable transfers.
|
|
2
|
+
|
|
3
|
+
This private module keeps disk state and path safety details out of the S3
|
|
4
|
+
request orchestration. network_volume_transfer re-exports the error type.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import unicodedata
|
|
11
|
+
from pathlib import Path, PurePosixPath
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
MIB = 1024 * 1024
|
|
15
|
+
PARTIAL_SUFFIX = ".rpdownload"
|
|
16
|
+
SIDECAR_SUFFIX = ".rpdownload.json"
|
|
17
|
+
# every file a download writes next to its target
|
|
18
|
+
TRANSFER_SUFFIXES = (PARTIAL_SUFFIX, SIDECAR_SUFFIX, SIDECAR_SUFFIX + ".tmp")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NetworkVolumeTransferError(RuntimeError):
|
|
22
|
+
"""a transfer failed. `resumable` says whether re-running can help."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, message: str, resumable: bool = False):
|
|
25
|
+
super().__init__(message)
|
|
26
|
+
self.resumable = resumable
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _read_json(path: Path) -> Optional[Dict[str, Any]]:
|
|
30
|
+
try:
|
|
31
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
32
|
+
except (OSError, ValueError):
|
|
33
|
+
return None
|
|
34
|
+
return data if isinstance(data, dict) else None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _write_json(path: Path, data: Dict[str, Any]) -> None:
|
|
38
|
+
"""replace path atomically, synced, so a crash leaves the old state or
|
|
39
|
+
the new one."""
|
|
40
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
tmp = path.with_name(path.name + ".tmp")
|
|
42
|
+
with open(tmp, "w", encoding="utf-8") as handle:
|
|
43
|
+
json.dump(data, handle)
|
|
44
|
+
handle.flush()
|
|
45
|
+
os.fsync(handle.fileno())
|
|
46
|
+
os.replace(tmp, path)
|
|
47
|
+
_sync_dir(path.parent)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _sync_dir(path: Path) -> None:
|
|
51
|
+
"""make a rename in path survive a power loss. best effort: windows
|
|
52
|
+
cannot open a directory."""
|
|
53
|
+
try:
|
|
54
|
+
fd = os.open(path, os.O_RDONLY)
|
|
55
|
+
except OSError:
|
|
56
|
+
return
|
|
57
|
+
try:
|
|
58
|
+
os.fsync(fd)
|
|
59
|
+
except OSError:
|
|
60
|
+
# some filesystems refuse fsync on a directory; the rename stands
|
|
61
|
+
pass
|
|
62
|
+
finally:
|
|
63
|
+
os.close(fd)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _md5_hex(path: Path, offset: int, length: int) -> str:
|
|
67
|
+
digest = hashlib.md5() # noqa: S324 - etag comparison, not security
|
|
68
|
+
with open(path, "rb") as handle:
|
|
69
|
+
handle.seek(offset)
|
|
70
|
+
while length > 0 and (block := handle.read(min(8 * MIB, length))):
|
|
71
|
+
digest.update(block)
|
|
72
|
+
length -= len(block)
|
|
73
|
+
return digest.hexdigest()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _etag(value: Any) -> str:
|
|
77
|
+
return str(value or "").strip('"')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _is_md5(etag: str) -> bool:
|
|
81
|
+
return len(etag) == 32 and all(c in "0123456789abcdef" for c in etag.lower())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _multipart_etag(parts: List[Dict[str, Any]]) -> str:
|
|
85
|
+
"""the etag s3 (and runpod, verified) gives an object assembled from
|
|
86
|
+
parts: md5 of the parts' binary md5s, then "-<count>". "" when a part
|
|
87
|
+
etag is not an md5."""
|
|
88
|
+
etags = [_etag(part["ETag"]) for part in parts]
|
|
89
|
+
if not all(_is_md5(etag) for etag in etags):
|
|
90
|
+
return ""
|
|
91
|
+
digest = hashlib.md5(b"".join(bytes.fromhex(etag) for etag in etags)) # noqa: S324
|
|
92
|
+
return f"{digest.hexdigest()}-{len(parts)}"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _same_version(a: os.stat_result, b: os.stat_result) -> bool:
|
|
96
|
+
return a.st_size == b.st_size and a.st_mtime_ns == b.st_mtime_ns
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _not_older(a: float, b: float) -> bool:
|
|
100
|
+
"""a >= b in whole seconds; s3 dates carry no finer precision."""
|
|
101
|
+
return int(a) >= int(b)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _check_remote_path(remote_path: Optional[str]) -> None:
|
|
105
|
+
if remote_path and ".." in remote_path.replace("\\", "/").split("/"):
|
|
106
|
+
raise ValueError(f"remote path {remote_path!r} must not contain '..'")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _check_local_names(targets: List[Path], root: Path) -> None:
|
|
110
|
+
"""refuse targets that would share a path on disk: two that are one name
|
|
111
|
+
on a case-insensitive or normalizing filesystem ("Model.bin" and
|
|
112
|
+
"model.bin"), a file whose name is also a folder another file lives in
|
|
113
|
+
("a" beside "A/b"), or a file whose partial, sidecar or sidecar temp name
|
|
114
|
+
is also a target or a folder one lives in ("x" beside "x.rpdownload/y").
|
|
115
|
+
names are compared nfc-normalized (apfs ignores normalization) and case
|
|
116
|
+
folded, which can refuse a listing a case-sensitive filesystem would
|
|
117
|
+
take: the safe direction."""
|
|
118
|
+
|
|
119
|
+
def fold(path: PurePosixPath) -> str:
|
|
120
|
+
return unicodedata.normalize("NFC", str(path)).casefold()
|
|
121
|
+
|
|
122
|
+
rels = [PurePosixPath(target.relative_to(root).as_posix()) for target in targets]
|
|
123
|
+
files: Dict[str, PurePosixPath] = {}
|
|
124
|
+
dirs: Dict[str, PurePosixPath] = {}
|
|
125
|
+
for rel in rels:
|
|
126
|
+
if fold(rel) in files:
|
|
127
|
+
raise NetworkVolumeTransferError(
|
|
128
|
+
f"cannot download both {files[fold(rel)]} and {rel}: they would be one "
|
|
129
|
+
"file on a case-insensitive or normalizing filesystem"
|
|
130
|
+
)
|
|
131
|
+
files[fold(rel)] = rel
|
|
132
|
+
dirs.update({fold(parent): rel for parent in rel.parents if str(parent) != "."})
|
|
133
|
+
for rel in rels:
|
|
134
|
+
if fold(rel) in dirs:
|
|
135
|
+
raise NetworkVolumeTransferError(
|
|
136
|
+
f"cannot download both {rel} and {dirs[fold(rel)]}: "
|
|
137
|
+
"the first would have to be a file and a folder"
|
|
138
|
+
)
|
|
139
|
+
for suffix in TRANSFER_SUFFIXES:
|
|
140
|
+
twin = fold(rel.with_name(rel.name + suffix))
|
|
141
|
+
other = files.get(twin) or dirs.get(twin)
|
|
142
|
+
if other is not None:
|
|
143
|
+
raise NetworkVolumeTransferError(
|
|
144
|
+
f"cannot download both {rel} and {other}: "
|
|
145
|
+
f"{rel.name + suffix} is used for the first's partial download"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _safe_target(root: Path, rel: str) -> Path:
|
|
150
|
+
"""local path for a remote relative key, refusing anything outside root.
|
|
151
|
+
"." and empty segments are refused too: "a/./b" would alias "a/b"."""
|
|
152
|
+
segments = rel.split("/")
|
|
153
|
+
if (
|
|
154
|
+
rel.startswith("/")
|
|
155
|
+
or "\\" in rel
|
|
156
|
+
or any(seg in ("..", ".", "") for seg in segments)
|
|
157
|
+
or ":" in segments[0]
|
|
158
|
+
):
|
|
159
|
+
raise NetworkVolumeTransferError(f"refusing unsafe remote path: {rel!r}")
|
|
160
|
+
target = root.joinpath(*segments)
|
|
161
|
+
if root.resolve() not in target.resolve().parents:
|
|
162
|
+
raise NetworkVolumeTransferError(f"refusing unsafe remote path: {rel!r}")
|
|
163
|
+
# a symlinked folder would let two remote paths share one local file,
|
|
164
|
+
# which the name collision check cannot see
|
|
165
|
+
for depth in range(1, len(segments)):
|
|
166
|
+
if root.joinpath(*segments[:depth]).is_symlink():
|
|
167
|
+
raise NetworkVolumeTransferError(
|
|
168
|
+
f"{root.joinpath(*segments[:depth])} is a symlink; "
|
|
169
|
+
"download into a folder without symlinked subfolders"
|
|
170
|
+
)
|
|
171
|
+
return target
|
|
@@ -87,6 +87,7 @@ async def run_graphql_query_async(
|
|
|
87
87
|
variables: Optional[Dict[str, Any]] = None,
|
|
88
88
|
timeout: float = 60.0,
|
|
89
89
|
anonymous: bool = False,
|
|
90
|
+
headers: Optional[Dict[str, str]] = None,
|
|
90
91
|
) -> Dict[str, Any]:
|
|
91
92
|
"""
|
|
92
93
|
Async variant of run_graphql_query, sharing the same url, headers,
|
|
@@ -109,7 +110,7 @@ async def run_graphql_query_async(
|
|
|
109
110
|
async with aiohttp.ClientSession(timeout=client_timeout) as session:
|
|
110
111
|
async with session.post(
|
|
111
112
|
_graphql_url(),
|
|
112
|
-
headers=_build_headers(effective_api_key),
|
|
113
|
+
headers={**_build_headers(effective_api_key), **(headers or {})},
|
|
113
114
|
json=_build_payload(query, variables),
|
|
114
115
|
) as response:
|
|
115
116
|
if response.status == HTTP_STATUS_UNAUTHORIZED:
|
|
@@ -92,6 +92,12 @@ mutation FinalizeArtifactUpload($input: FinalizeFlashArtifactUploadInput!) {
|
|
|
92
92
|
}
|
|
93
93
|
"""
|
|
94
94
|
|
|
95
|
+
MUTATION_FINALIZE_TASK_ARTIFACT_UPLOAD = """
|
|
96
|
+
mutation FinalizeArtifactUpload($input: FinalizeFlashArtifactUploadInput!) {
|
|
97
|
+
finalizeFlashArtifactUpload(input: $input) { id manifest resourceSpec }
|
|
98
|
+
}
|
|
99
|
+
"""
|
|
100
|
+
|
|
95
101
|
MUTATION_DEPLOY_BUILD = """
|
|
96
102
|
mutation deployBuildToEnvironment($input: DeployBuildToEnvironmentInput!) {
|
|
97
103
|
deployBuildToEnvironment(input: $input) { id name }
|
|
@@ -15,7 +15,7 @@ from .job import Job
|
|
|
15
15
|
from .markers import delete, get, init, patch, post, put
|
|
16
16
|
from .schedule import schedule
|
|
17
17
|
from .spec import ResourceKind, ResourceSpec, RouteSpec
|
|
18
|
-
from .stubs import Api, Queue
|
|
18
|
+
from .stubs import Api, Queue, Task
|
|
19
19
|
from .model import Model
|
|
20
20
|
from .secret import Secret
|
|
21
21
|
from .volume import GlobalVolume, NetworkVolume, Volume
|
|
@@ -34,6 +34,7 @@ __all__ = [
|
|
|
34
34
|
"Model",
|
|
35
35
|
"NetworkVolume",
|
|
36
36
|
"Queue",
|
|
37
|
+
"Task",
|
|
37
38
|
"Secret",
|
|
38
39
|
"Volume",
|
|
39
40
|
"RemoteExecutionError",
|
|
@@ -20,6 +20,46 @@ from ..error import AuthenticationError, QueryError
|
|
|
20
20
|
_TRANSPORT_RETRIES = 4
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
class FlashTaskApiClient:
|
|
24
|
+
"""named task lifecycle operations with runtime-scoped authentication."""
|
|
25
|
+
|
|
26
|
+
async def _execute(self, query: str, task_input: Dict[str, Any]) -> Dict[str, Any]:
|
|
27
|
+
from .utils.network import api_key, runtime_headers
|
|
28
|
+
|
|
29
|
+
headers = runtime_headers()
|
|
30
|
+
response = await run_graphql_query_async(
|
|
31
|
+
query,
|
|
32
|
+
variables={"input": task_input},
|
|
33
|
+
api_key=(headers["Authorization"][7:] if headers else api_key()),
|
|
34
|
+
headers=headers,
|
|
35
|
+
)
|
|
36
|
+
return response["data"]
|
|
37
|
+
|
|
38
|
+
async def create(self, task_input: Dict[str, Any]) -> Dict[str, Any]:
|
|
39
|
+
from .utils.network import validate_runtime_target
|
|
40
|
+
|
|
41
|
+
validate_runtime_target(
|
|
42
|
+
task_input.get("flashAppName"), task_input.get("flashEnvironmentName")
|
|
43
|
+
)
|
|
44
|
+
data = await self._execute(
|
|
45
|
+
"""mutation FlashTaskCreate($input: FlashTaskCreateInput!) {
|
|
46
|
+
flashTaskCreate(input: $input) { podId flashBuildId resourceName }
|
|
47
|
+
}""",
|
|
48
|
+
task_input,
|
|
49
|
+
)
|
|
50
|
+
return data["flashTaskCreate"]
|
|
51
|
+
|
|
52
|
+
async def terminate(self, pod_id: str, token: str) -> None:
|
|
53
|
+
data = await self._execute(
|
|
54
|
+
"""mutation FlashTaskTerminate($input: FlashTaskTerminateInput!) {
|
|
55
|
+
flashTaskTerminate(input: $input)
|
|
56
|
+
}""",
|
|
57
|
+
{"podId": pod_id, "executionToken": token},
|
|
58
|
+
)
|
|
59
|
+
if data.get("flashTaskTerminate") is not True:
|
|
60
|
+
raise RuntimeError("task termination was not acknowledged")
|
|
61
|
+
|
|
62
|
+
|
|
23
63
|
def _rest_endpoint_input(endpoint: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
24
64
|
"""translate only saves whose capabilities rest can preserve atomically."""
|
|
25
65
|
fields = {
|
|
@@ -583,6 +623,8 @@ class AppsApiClient:
|
|
|
583
623
|
self, app_id: str, object_key: str, manifest: Dict[str, Any]
|
|
584
624
|
) -> Dict[str, Any]:
|
|
585
625
|
mutation = app_mutations.MUTATION_FINALIZE_ARTIFACT_UPLOAD
|
|
626
|
+
if any(r.get("kind") == "task" for r in manifest.get("resources", [])):
|
|
627
|
+
mutation = app_mutations.MUTATION_FINALIZE_TASK_ARTIFACT_UPLOAD
|
|
586
628
|
data = await self._execute(
|
|
587
629
|
mutation,
|
|
588
630
|
{
|
|
@@ -151,7 +151,11 @@ class App:
|
|
|
151
151
|
accelerate_downloads: bool = True,
|
|
152
152
|
container_disk_gb: Optional[int] = None,
|
|
153
153
|
) -> Callable[[Callable], FunctionHandle]:
|
|
154
|
-
"""declare ephemeral pod compute from a function.
|
|
154
|
+
"""declare ephemeral pod compute from a function.
|
|
155
|
+
|
|
156
|
+
remote task arguments and results use json in every execution
|
|
157
|
+
context. use .local() for direct python execution without transport.
|
|
158
|
+
"""
|
|
155
159
|
|
|
156
160
|
def decorator(fn: Callable) -> FunctionHandle:
|
|
157
161
|
spec = ResourceSpec(
|
|
@@ -259,6 +263,12 @@ class App:
|
|
|
259
263
|
"""resolve a resource spec to an invocation target."""
|
|
260
264
|
spec.validate()
|
|
261
265
|
if spec.kind is ResourceKind.TASK:
|
|
266
|
+
from .context import deployed_identity
|
|
267
|
+
from .targets import DeployedTaskTarget
|
|
268
|
+
|
|
269
|
+
identity = deployed_identity()
|
|
270
|
+
if identity is not None:
|
|
271
|
+
return DeployedTaskTarget(self.name, identity[1], spec.name)
|
|
262
272
|
handle = self._resources.get(spec.name)
|
|
263
273
|
fn = getattr(handle, "_fn", None)
|
|
264
274
|
return PodTarget(
|