runpod 2.2.0__tar.gz → 2.2.1__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.2.0 → runpod-2.2.1}/CHANGELOG.md +16 -0
- {runpod-2.2.0/runpod.egg-info → runpod-2.2.1}/PKG-INFO +2 -2
- {runpod-2.2.0 → runpod-2.2.1}/README.md +1 -1
- runpod-2.2.1/examples/README.md +63 -0
- runpod-2.2.1/examples/api/create_container_registry_auth.py +24 -0
- runpod-2.2.1/examples/api/create_endpoint.py +44 -0
- runpod-2.2.1/examples/api/create_template.py +27 -0
- runpod-2.2.1/examples/api/get_endpoints.py +13 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/endpoints/asyncio_job_request.py +7 -7
- {runpod-2.2.0 → runpod-2.2.1}/examples/endpoints/health.py +2 -2
- runpod-2.2.1/examples/endpoints/run.py +23 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/endpoints/run_sync.py +7 -3
- runpod-2.2.1/examples/endpoints/streaming.py +24 -0
- runpod-2.2.1/examples/rest_wrapper.py +47 -0
- runpod-2.2.1/examples/serverless/concurrent_handler.py +41 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/serverless/logger.py +17 -7
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_version.py +3 -3
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/queries/apps.py +2 -2
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/auth.py +83 -18
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/build.py +86 -1
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/deploy.py +37 -2
- {runpod-2.2.0 → runpod-2.2.1}/runpod/endpoint/asyncio/asyncio_runner.py +12 -3
- {runpod-2.2.0 → runpod-2.2.1}/runpod/endpoint/helpers.py +10 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/endpoint/runner.py +25 -4
- {runpod-2.2.0 → runpod-2.2.1}/runpod/error.py +15 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/main.py +50 -27
- runpod-2.2.1/runpod/version.py +33 -0
- {runpod-2.2.0 → runpod-2.2.1/runpod.egg-info}/PKG-INFO +2 -2
- {runpod-2.2.0 → runpod-2.2.1}/runpod.egg-info/SOURCES.txt +2 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod.egg-info/scm_file_list.json +2 -0
- runpod-2.2.1/runpod.egg-info/scm_version.json +8 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_agent_auth.py +103 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_build.py +60 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_deploy.py +81 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_gpu_catalog.py +17 -7
- runpod-2.2.1/tests/test_cli/test_agent_login.py +396 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_endpoint/test_asyncio_runner.py +36 -0
- runpod-2.2.1/tests/test_endpoint/test_failed_jobs.py +89 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_endpoint/test_runner.py +14 -0
- runpod-2.2.1/tests/test_version.py +132 -0
- runpod-2.2.0/examples/api/create_container_registry_auth.py +0 -19
- runpod-2.2.0/examples/api/create_endpoint.py +0 -29
- runpod-2.2.0/examples/api/create_template.py +0 -16
- runpod-2.2.0/examples/api/get_endpoints.py +0 -7
- runpod-2.2.0/examples/endpoints/run.py +0 -24
- runpod-2.2.0/examples/endpoints/streaming.py +0 -20
- runpod-2.2.0/examples/rest_wrapper.py +0 -37
- runpod-2.2.0/examples/serverless/concurrent_handler.py +0 -28
- runpod-2.2.0/runpod/version.py +0 -14
- runpod-2.2.0/runpod.egg-info/scm_version.json +0 -8
- runpod-2.2.0/tests/test_cli/test_agent_login.py +0 -185
- runpod-2.2.0/tests/test_version.py +0 -22
- {runpod-2.2.0 → runpod-2.2.1}/.dockerignore +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/dependabot.yml +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/tape_collection/cli_config.tape +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/tape_collection/cli_help.tape +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/tape_collection/cli_ssh.tape +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/tests.json +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/workflows/cd.yml +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/workflows/ci.yml +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.github/workflows/vhs.yml +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/.gitignore +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/ARCHITECTURE.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/CONTRIBUTING.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/LICENSE +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/MANIFEST.in +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/Makefile +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/SECURITY.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/build_tools/compile_gpu_test.sh +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/build_tools/gpu_test.c +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/api/handling_errors.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/api/queries.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/demos/config.gif +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/demos/help.gif +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/demos/ssh.gif +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/references/command_line_interface.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/references/projects.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/references/runpod.toml.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/cli/start_here.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/getting_started.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/images/env_var_location.png +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/functions/error_handling.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/functions/logging.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/gpu_binary_compilation.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/local_testing.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/utils/rp_cleanup.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/utils/rp_debugger.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/utils/rp_download.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/utils/rp_upload.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/utils/rp_validator.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/volume_cache.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/worker.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/worker_fitness_checks.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/docs/serverless/worker_realtime.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/apps/README.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/apps/gpu_inference.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/apps/hello_world.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/apps/streaming.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/apps/train_and_eval.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/apps/web_service.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/serverless/simple_handler.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/examples/serverless/volume_cache_handler.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/pyproject.toml +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/pytest.ini +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/requirements.txt +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_binary_helpers.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_health/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_health/cuda.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_health/fitness.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_health/gpu.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_health/system.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_hosts.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_logger.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_serialization.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_startup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/_volume_transfer_utils.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/agent.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/_rate_limit.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/ctl_commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/graphql.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/mutations/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/mutations/apps.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/mutations/container_register_auth.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/queries/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/queries/user.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/api/rest.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/api.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/app.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/context.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/datacenter.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/dev.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/discovery.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/discovery_state.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/entrypoint.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/errors.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/gpu.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/handles.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/images.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/init.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/invoker.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/job.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/logs.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/manage.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/markers.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/model.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/monitor.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/placement.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/protocol.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/registry.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/schedule.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/secret.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/serialization.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/shim.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/spec.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/stubs.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/targets.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/tasks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/utils/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/utils/client.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/utils/events.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/utils/lookup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/utils/names.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/utils/network.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/volume.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/apps/watch.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/entry.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/config/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/config/functions.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/pod/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/pod/commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/ssh/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/ssh/commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/groups/ssh/functions.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/utils/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/utils/rp_info.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/utils/rp_runpodignore.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/utils/rp_userspace.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/cli/utils/ssh_cmd.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/endpoint/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/endpoint/asyncio/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/http_client.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/network_volume_transfer.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/api.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/catalog.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/compute.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/console.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/endpoint.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/endpoint_config.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/logs.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/options.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/output.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/pod.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/update.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/rp_cli/volume.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/binaries/README.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/binaries/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/binaries/gpu_test +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_capture.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_fastapi.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_fitness.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_gpu_fitness.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_handler.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_http.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_job.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_local.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_logger.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_ping.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_prestart.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_progress.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_scale.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_system_fitness.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/rp_tips.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/modules/worker_state.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_cleanup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_cuda.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_debugger.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_download.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_model_cache.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_ssrf.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_upload.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_validator.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/utils/rp_volume_cache.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/serverless/worker.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod/user_agent.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod.egg-info/dependency_links.txt +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod.egg-info/entry_points.txt +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod.egg-info/requires.txt +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/runpod.egg-info/top_level.txt +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/scripts/README.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/scripts/benchmark_cold_start.sh +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/scripts/compare_benchmarks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/setup.cfg +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/setup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/apps_live_smoke.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/conftest.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/e2e_provisioner.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/01_hello_world.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/02_invocation_styles.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/03_gpu.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/04_dependencies.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/05_api_service.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/06_tasks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/07_pipelines.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/08_volumes.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/09_secrets.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/10_cached_models.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/11_train_eval.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/12_custom_image.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/13_streaming.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/README.md +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/examples/run_all.sh +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/fixtures/cold_start/handler.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/fixtures/cold_start/pyproject.toml +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/matrix/main.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/matrix/run.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/task_pod_smoke.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/test_cold_start.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/test_mock_worker.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/test_network_volume_transfer_live.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/e2e/tests.json +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/fixtures/mock_gpu_test +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_agent.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_api/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_api/test_ctl_commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_api/test_rate_limit.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_api/test_rest.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/conftest.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_api_client.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_app.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_auth.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_compat_lock.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_config_params.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_deployed_tasks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_deployment_validation.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_dev.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_discovery.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_dispatch.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_images.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_job.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_logs.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_manage.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_markers.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_model.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_monitor.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_placement.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_registry.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_retry.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_runtime_call_validation.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_secret.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_shim.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_targets.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_tasks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_transport_parity.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_volume.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_apps/test_watch.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_catalog_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_api.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_groups/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_groups/test_config_functions.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_groups/test_pod_commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_groups/test_ssh_commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_groups/test_ssh_dispatch.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_groups/test_ssh_functions.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_logs.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_options.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_output.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_utils/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_utils/test_info.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_utils/test_runpodignore.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_utils/test_ssh_cmd.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_cli_utils/test_userspace.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_compute.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_console.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_deploy_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_endpoint_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_init_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_management_commands.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_pod_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_update_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_cli/test_volume_command.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_endpoint/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_endpoint/test_asyncio_init.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_endpoint/test_init.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_error.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_hosts.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_init.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_network_volume_transfer.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_performance/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_performance/test_cold_start.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_profile.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serialization.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/local_sim/.env_example +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/local_sim/Makefile +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/local_sim/localhost.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/local_sim/worker.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_capture.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_init.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_integration_worker_state.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fastapi.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/conftest.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_force_kill.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_gpu_checks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_gpu_integration.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_registration.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_safe_startup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_startup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_fitness/test_system_checks.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_handler.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_http.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_job.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_local.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_logger.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_ping.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_progress.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_scale.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_state.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_modules/test_tips.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_prestart.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_prestart_lifecycle.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_refresh_worker.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_rp_scale.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_scale_mirror.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_cleanup.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_cuda.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_debugger.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_download.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_model_cache.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_rp_volume_cache.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_ssrf.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_upload.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils/test_validate.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_utils_init.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_serverless/test_worker.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_shared/__init__.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_shared/test_auth.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_user_agent.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/test_whatever.py +0 -0
- {runpod-2.2.0 → runpod-2.2.1}/tests/whatever.py +0 -0
|
@@ -22,6 +22,22 @@
|
|
|
22
22
|
* Deployment artifacts apply project ignore rules and mandatory secret-file exclusions while preserving vendored dependency assets.
|
|
23
23
|
* Synchronous calls wait across polling timeouts on Python 3.10 and propagate operation timeouts.
|
|
24
24
|
|
|
25
|
+
## [2.2.1](https://github.com/runpod/runpod-python/compare/v2.2.0...v2.2.1) (2026-11-10)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* **apps:** a `runpod` pin in `dependencies=[...]` silently produces a mixed install on workers ([#700](https://github.com/runpod/runpod-python/issues/700)) ([16a0280](https://github.com/runpod/runpod-python/commit/16a0280085f7d3fee2f75ba1aa946a50d34ac854))
|
|
31
|
+
* **apps:** give new deployed endpoints unique template names ([#696](https://github.com/runpod/runpod-python/issues/696)) ([c930098](https://github.com/runpod/runpod-python/commit/c930098a02540e327a695a855b49026c73508c14))
|
|
32
|
+
* **cli:** make agent login a two-step flow ([#711](https://github.com/runpod/runpod-python/issues/711)) ([cbf681b](https://github.com/runpod/runpod-python/commit/cbf681b3c5cd4b3a42964472fe19aa1c5f100221))
|
|
33
|
+
* **endpoint:** Endpoint.run_sync drops the error of a FAILED job ([#699](https://github.com/runpod/runpod-python/issues/699)) ([05a77b3](https://github.com/runpod/runpod-python/commit/05a77b3370fec18f6ba2f6201dbd8b585ee22260))
|
|
34
|
+
* **version:** report the imported tree's version ([#708](https://github.com/runpod/runpod-python/issues/708)) ([13dfbe5](https://github.com/runpod/runpod-python/commit/13dfbe57ad36fbe8cb53a2d788cab88951f437e9))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Documentation
|
|
38
|
+
|
|
39
|
+
* **examples:** fix broken and stale examples under examples/ ([#694](https://github.com/runpod/runpod-python/issues/694)) ([18d5fee](https://github.com/runpod/runpod-python/commit/18d5fee7a0612f800decb00d57d05fe439ca2f2c))
|
|
40
|
+
|
|
25
41
|
## [2.2.0](https://github.com/runpod/runpod-python/compare/v2.1.0...v2.2.0) (2026-11-09)
|
|
26
42
|
|
|
27
43
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runpod
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: 🐍 | Python library for Runpod API and serverless worker SDK.
|
|
5
5
|
Home-page: https://runpod.io
|
|
6
6
|
Author: Runpod
|
|
@@ -67,7 +67,7 @@ Dynamic: requires-python
|
|
|
67
67
|
[](https://github.com/runpod/runpod-python/actions/workflows/CI-pytests.yml)
|
|
68
68
|
[](LICENSE)
|
|
69
69
|
|
|
70
|
-
[Documentation](https://docs.runpod.io) • [Examples](examples
|
|
70
|
+
[Documentation](https://docs.runpod.io) • [Examples](examples) • [Discord](https://discord.gg/pJ3P2DbUUq)
|
|
71
71
|
|
|
72
72
|
</div>
|
|
73
73
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://github.com/runpod/runpod-python/actions/workflows/CI-pytests.yml)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
|
|
12
|
-
[Documentation](https://docs.runpod.io) • [Examples](examples
|
|
12
|
+
[Documentation](https://docs.runpod.io) • [Examples](examples) • [Discord](https://discord.gg/pJ3P2DbUUq)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
## Before you start
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install runpod
|
|
7
|
+
rp login
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
`rp login` stores your API key, and every example picks it up from there. If
|
|
11
|
+
`RUNPOD_API_KEY` is set in your environment, the examples use that key
|
|
12
|
+
instead.
|
|
13
|
+
|
|
14
|
+
## Which folder to start with
|
|
15
|
+
|
|
16
|
+
| Folder | What it shows | Needs |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| [`apps/`](apps) | Python functions that run on cloud GPUs and CPUs, started with `rp flash dev` | An account. Start here if you are new. |
|
|
19
|
+
| [`serverless/`](serverless) | Handlers for your own serverless worker image | Nothing. They run locally. |
|
|
20
|
+
| [`endpoints/`](endpoints) | Sending jobs to an existing serverless endpoint | An endpoint id |
|
|
21
|
+
| [`api/`](api) | Creating templates, endpoints, and registry credentials | An account |
|
|
22
|
+
| [`rest_wrapper.py`](rest_wrapper.py) | Pod lifecycle: create, stop, resume, terminate | An account. Creates a billable GPU pod. |
|
|
23
|
+
|
|
24
|
+
## Running the serverless handlers locally
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cd examples/serverless
|
|
28
|
+
python simple_handler.py --test_input '{"input": {"name": "world"}}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or start a local API server and call it:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python simple_handler.py --rp_serve_api
|
|
35
|
+
curl -X POST http://localhost:8000/runsync \
|
|
36
|
+
-H "Content-Type: application/json" \
|
|
37
|
+
-d '{"input": {"name": "world"}}'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Run with no arguments, a handler looks for a `test_input.json` file in the
|
|
41
|
+
current directory.
|
|
42
|
+
|
|
43
|
+
## Getting an endpoint id for `endpoints/`
|
|
44
|
+
|
|
45
|
+
Each `endpoints/` example has a `YOUR_ENDPOINT_ID` placeholder. To use an
|
|
46
|
+
endpoint you already have, list them with `rp endpoint list`. To create one
|
|
47
|
+
for trying the examples, run [`api/create_endpoint.py`](api/create_endpoint.py),
|
|
48
|
+
which deploys `runpod/mock-worker` and prints the new id.
|
|
49
|
+
|
|
50
|
+
`endpoints/streaming.py` needs a worker that streams: a generator handler, or
|
|
51
|
+
`runpod/mock-worker` started with `--generator`.
|
|
52
|
+
|
|
53
|
+
## Cost and cleanup
|
|
54
|
+
|
|
55
|
+
- Endpoints with `workers_min=0` cost nothing while idle. Delete them with
|
|
56
|
+
`rp endpoint delete ENDPOINT_ID`.
|
|
57
|
+
- Templates stay on your account. Delete them from the Templates page in the
|
|
58
|
+
console.
|
|
59
|
+
- `rest_wrapper.py` terminates its pod at the end, including when a step
|
|
60
|
+
fails.
|
|
61
|
+
- `rp flash dev` deletes the endpoints it created when the session ends.
|
|
62
|
+
Network volumes created by `apps/` examples are not deleted; list them with
|
|
63
|
+
`rp volume list` and remove them with `rp volume delete`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Example of creating a container registry auth with the Runpod API.
|
|
2
|
+
|
|
3
|
+
Registry credentials let templates pull images from a private registry. The
|
|
4
|
+
credential stays on your account after the script exits; delete it with
|
|
5
|
+
`rp registry delete NAME`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import runpod
|
|
9
|
+
|
|
10
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
11
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
new_container_registry_auth = runpod.create_container_registry_auth(
|
|
15
|
+
name="example-registry-auth",
|
|
16
|
+
username="YOUR_REGISTRY_USERNAME",
|
|
17
|
+
password="YOUR_REGISTRY_PASSWORD",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
print(new_container_registry_auth)
|
|
21
|
+
|
|
22
|
+
except runpod.error.QueryError as err:
|
|
23
|
+
print(err)
|
|
24
|
+
print(err.query)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Example of creating an endpoint with the Runpod API.
|
|
2
|
+
|
|
3
|
+
The endpoint scales to zero workers when idle, so it costs nothing until it
|
|
4
|
+
receives requests. It stays on your account after the script exits; delete it
|
|
5
|
+
with `rp endpoint delete ENDPOINT_ID`, then delete its template from the
|
|
6
|
+
Templates page in the console.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import uuid
|
|
10
|
+
|
|
11
|
+
import runpod
|
|
12
|
+
|
|
13
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
14
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
15
|
+
|
|
16
|
+
# Template and endpoint names must be unique on your account.
|
|
17
|
+
NAME = f"example-endpoint-{uuid.uuid4().hex[:8]}"
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
|
|
21
|
+
# runpod/mock-worker echoes its input back, which is enough to try the
|
|
22
|
+
# examples in examples/endpoints/.
|
|
23
|
+
new_template = runpod.create_template(
|
|
24
|
+
name=NAME, image_name="runpod/mock-worker:latest", is_serverless=True
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
print(new_template)
|
|
28
|
+
|
|
29
|
+
new_endpoint = runpod.create_endpoint(
|
|
30
|
+
name=NAME,
|
|
31
|
+
template_id=new_template["id"],
|
|
32
|
+
gpu_ids="AMPERE_16",
|
|
33
|
+
workers_min=0,
|
|
34
|
+
workers_max=1,
|
|
35
|
+
flashboot=True,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
print(new_endpoint)
|
|
39
|
+
print(f"Endpoint id: {new_endpoint['id']}")
|
|
40
|
+
print(f"Delete it with: rp endpoint delete {new_endpoint['id']}")
|
|
41
|
+
|
|
42
|
+
except runpod.error.QueryError as err:
|
|
43
|
+
print(err)
|
|
44
|
+
print(err.query)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Example of creating a template with the Runpod API.
|
|
2
|
+
|
|
3
|
+
The template stays on your account after the script exits. Delete it from
|
|
4
|
+
the Templates page in the console when you no longer need it.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import uuid
|
|
8
|
+
|
|
9
|
+
import runpod
|
|
10
|
+
|
|
11
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
12
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
13
|
+
|
|
14
|
+
# Template names must be unique on your account.
|
|
15
|
+
TEMPLATE_NAME = f"example-template-{uuid.uuid4().hex[:8]}"
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
|
|
19
|
+
new_template = runpod.create_template(
|
|
20
|
+
name=TEMPLATE_NAME, image_name="runpod/base:2.0.0-ubuntu2404"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
print(new_template)
|
|
24
|
+
|
|
25
|
+
except runpod.error.QueryError as err:
|
|
26
|
+
print(err)
|
|
27
|
+
print(err.query)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Example of listing your serverless endpoints with the Runpod API."""
|
|
2
|
+
|
|
3
|
+
import runpod
|
|
4
|
+
|
|
5
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
6
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
7
|
+
|
|
8
|
+
endpoints = runpod.get_endpoints()
|
|
9
|
+
|
|
10
|
+
# Each entry is the endpoint's full configuration, including its environment
|
|
11
|
+
# variables, so print only the id and name here.
|
|
12
|
+
for endpoint in endpoints:
|
|
13
|
+
print(endpoint["id"], endpoint.get("name"))
|
|
@@ -7,11 +7,10 @@ import asyncio
|
|
|
7
7
|
import runpod
|
|
8
8
|
from runpod import AsyncioEndpoint, AsyncioJob, http_client
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
) # For Windows Users
|
|
10
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
11
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
JOB_TIMEOUT = 120 # seconds
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
async def main():
|
|
@@ -21,7 +20,7 @@ async def main():
|
|
|
21
20
|
async with http_client.AsyncClientSession() as session:
|
|
22
21
|
# Invoke API
|
|
23
22
|
payload = {}
|
|
24
|
-
endpoint = AsyncioEndpoint("
|
|
23
|
+
endpoint = AsyncioEndpoint("YOUR_ENDPOINT_ID", session)
|
|
25
24
|
job: AsyncioJob = await endpoint.run(payload)
|
|
26
25
|
|
|
27
26
|
# Get current job status
|
|
@@ -30,8 +29,9 @@ async def main():
|
|
|
30
29
|
# Print status
|
|
31
30
|
print(status)
|
|
32
31
|
|
|
33
|
-
# Wait until job
|
|
34
|
-
|
|
32
|
+
# Wait until the job completes or fails, up to JOB_TIMEOUT seconds.
|
|
33
|
+
# Raises TimeoutError if the job is still running after that.
|
|
34
|
+
output = await job.output(timeout=JOB_TIMEOUT)
|
|
35
35
|
|
|
36
36
|
# Print output
|
|
37
37
|
print(output)
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import runpod
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
6
6
|
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
7
7
|
|
|
8
|
-
endpoint = runpod.Endpoint("
|
|
8
|
+
endpoint = runpod.Endpoint("YOUR_ENDPOINT_ID")
|
|
9
9
|
|
|
10
10
|
endpoint_health = endpoint.health()
|
|
11
11
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example of calling an endpoint using the Runpod Python Language Library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import runpod
|
|
6
|
+
|
|
7
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
8
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
9
|
+
|
|
10
|
+
endpoint = runpod.Endpoint("YOUR_ENDPOINT_ID")
|
|
11
|
+
|
|
12
|
+
run_request = endpoint.run(
|
|
13
|
+
{"input": {"prompt": "a photo of a horse the size of a Boeing 787"}}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Check the status of the run request, for example IN_QUEUE or IN_PROGRESS.
|
|
17
|
+
print(run_request.status())
|
|
18
|
+
|
|
19
|
+
# Wait up to 60 seconds for the job to finish, then print its output.
|
|
20
|
+
# Raises TimeoutError if the job is still running after that.
|
|
21
|
+
# Without a timeout, output() returns immediately, and returns None if the
|
|
22
|
+
# job has not finished yet.
|
|
23
|
+
print(run_request.output(timeout=60))
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
Example of calling an endpoint using the Runpod Python Language Library.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import requests
|
|
6
|
+
|
|
5
7
|
import runpod
|
|
6
8
|
|
|
7
|
-
#
|
|
9
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
8
10
|
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
9
11
|
|
|
10
|
-
endpoint = runpod.Endpoint("
|
|
12
|
+
endpoint = runpod.Endpoint("YOUR_ENDPOINT_ID")
|
|
11
13
|
|
|
12
14
|
try:
|
|
13
15
|
# Run the endpoint synchronously, blocking until the endpoint run is complete.
|
|
@@ -17,5 +19,7 @@ try:
|
|
|
17
19
|
)
|
|
18
20
|
|
|
19
21
|
print(run_request)
|
|
20
|
-
except TimeoutError:
|
|
22
|
+
except (TimeoutError, requests.exceptions.Timeout):
|
|
23
|
+
# TimeoutError: the job was still running when the timeout expired.
|
|
24
|
+
# requests.exceptions.Timeout: the HTTP request itself timed out.
|
|
21
25
|
print("Job timed out.")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
""" Example of streaming data from an endpoint. """
|
|
2
|
+
|
|
3
|
+
import runpod
|
|
4
|
+
|
|
5
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
6
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
7
|
+
|
|
8
|
+
# This input targets an endpoint running the runpod/mock-worker image started
|
|
9
|
+
# with --generator, which yields each item of mock_return with mock_delay
|
|
10
|
+
# seconds between them. Replace it with the input your own streaming
|
|
11
|
+
# handler expects.
|
|
12
|
+
endpoint = runpod.Endpoint("YOUR_ENDPOINT_ID")
|
|
13
|
+
|
|
14
|
+
run_request = endpoint.run(
|
|
15
|
+
{
|
|
16
|
+
"input": {
|
|
17
|
+
"mock_return": ["a", "b", "c", "d", "e", "f", "g"],
|
|
18
|
+
"mock_delay": 1,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
for output in run_request.stream():
|
|
24
|
+
print(output)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Create, stop, resume, and terminate a pod with the Runpod API.
|
|
2
|
+
|
|
3
|
+
This creates a billable GPU pod. The pod is terminated at the end, including
|
|
4
|
+
when a step fails.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
import runpod
|
|
10
|
+
|
|
11
|
+
# Run `rp login` once to store your API key, or uncomment the line below:
|
|
12
|
+
# runpod.api_key = "YOUR_RUNPOD_API_KEY"
|
|
13
|
+
|
|
14
|
+
GPU_TYPE = "NVIDIA GeForce RTX 4090" # see `rp catalog` for other GPU types
|
|
15
|
+
WAIT_SECONDS = 10
|
|
16
|
+
|
|
17
|
+
gpus = runpod.get_gpus()
|
|
18
|
+
print(gpus)
|
|
19
|
+
|
|
20
|
+
gpu = runpod.get_gpu(GPU_TYPE)
|
|
21
|
+
print(gpu)
|
|
22
|
+
|
|
23
|
+
pod = runpod.create_pod(
|
|
24
|
+
"example-pod",
|
|
25
|
+
"runpod/pytorch:1.0.2-cu1281-torch280-ubuntu2404",
|
|
26
|
+
GPU_TYPE,
|
|
27
|
+
)
|
|
28
|
+
print(pod)
|
|
29
|
+
pod_id = pod["id"]
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
print("Waiting for pod to be created...")
|
|
33
|
+
time.sleep(WAIT_SECONDS)
|
|
34
|
+
|
|
35
|
+
print(runpod.stop_pod(pod_id))
|
|
36
|
+
|
|
37
|
+
print("Waiting for pod to be stopped...")
|
|
38
|
+
time.sleep(WAIT_SECONDS)
|
|
39
|
+
|
|
40
|
+
# Resuming can fail if no GPU of this type is free on the pod's machine.
|
|
41
|
+
print(runpod.resume_pod(pod_id))
|
|
42
|
+
|
|
43
|
+
print("Waiting for pod to be resumed...")
|
|
44
|
+
time.sleep(WAIT_SECONDS)
|
|
45
|
+
finally:
|
|
46
|
+
runpod.terminate_pod(pod_id)
|
|
47
|
+
print(f"Terminated pod {pod_id}")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Concurrency Modifier Example
|
|
2
|
+
|
|
3
|
+
An async handler lets one worker run several jobs at once. The worker calls
|
|
4
|
+
the concurrency modifier with its current concurrency and resizes to the value
|
|
5
|
+
it returns, waiting until no jobs are in flight before resizing.
|
|
6
|
+
|
|
7
|
+
To test locally, run:
|
|
8
|
+
python concurrent_handler.py --test_input '{"input": {"seconds": 1}}'
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
|
|
13
|
+
import runpod
|
|
14
|
+
|
|
15
|
+
MAX_CONCURRENCY = 4
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ---------------------------------- Handler --------------------------------- #
|
|
19
|
+
async def async_handler(job):
|
|
20
|
+
"""
|
|
21
|
+
Async handler. While it awaits, the worker can start other jobs.
|
|
22
|
+
"""
|
|
23
|
+
seconds = job["input"].get("seconds", 1)
|
|
24
|
+
await asyncio.sleep(seconds)
|
|
25
|
+
return {"slept": seconds}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# --------------------------- Concurrency Modifier --------------------------- #
|
|
29
|
+
def concurrency_modifier(current_concurrency: int) -> int:
|
|
30
|
+
"""
|
|
31
|
+
Return the number of jobs this worker should run at once.
|
|
32
|
+
|
|
33
|
+
Replace the fixed value with your own logic, for example based on free
|
|
34
|
+
GPU memory or queue depth.
|
|
35
|
+
"""
|
|
36
|
+
return MAX_CONCURRENCY
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
runpod.serverless.start(
|
|
40
|
+
{"handler": async_handler, "concurrency_modifier": concurrency_modifier}
|
|
41
|
+
)
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Example of using the RunPodLogger class.
|
|
2
|
+
|
|
3
|
+
Running locally, the logger prints plain text. On a deployed worker, where
|
|
4
|
+
RUNPOD_ENDPOINT_ID is set, it prints one JSON object per line instead.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
import runpod
|
|
4
8
|
|
|
@@ -11,11 +15,11 @@ log.info("An info message")
|
|
|
11
15
|
log.warn("A warning message")
|
|
12
16
|
log.error("An error message")
|
|
13
17
|
|
|
14
|
-
#
|
|
15
|
-
# DEBUG
|
|
16
|
-
# INFO
|
|
17
|
-
# WARN
|
|
18
|
-
# ERROR
|
|
18
|
+
# Local output:
|
|
19
|
+
# DEBUG | A debug message
|
|
20
|
+
# INFO | An info message
|
|
21
|
+
# WARN | A warning message
|
|
22
|
+
# ERROR | An error message
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
log.debug("A debug message", request_id=JOB_ID)
|
|
@@ -23,7 +27,13 @@ log.info("An info message", request_id=JOB_ID)
|
|
|
23
27
|
log.warn("A warning message", request_id=JOB_ID)
|
|
24
28
|
log.error("An error message", request_id=JOB_ID)
|
|
25
29
|
|
|
26
|
-
#
|
|
30
|
+
# Local output:
|
|
31
|
+
# DEBUG | 1234567890 | A debug message
|
|
32
|
+
# INFO | 1234567890 | An info message
|
|
33
|
+
# WARN | 1234567890 | A warning message
|
|
34
|
+
# ERROR | 1234567890 | An error message
|
|
35
|
+
|
|
36
|
+
# Output on a deployed worker:
|
|
27
37
|
# {"requestId": "1234567890", "message": "A debug message", "level": "DEBUG"}
|
|
28
38
|
# {"requestId": "1234567890", "message": "An info message", "level": "INFO"}
|
|
29
39
|
# {"requestId": "1234567890", "message": "A warning message", "level": "WARN"}
|
|
@@ -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.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 2,
|
|
21
|
+
__version__ = version = '2.2.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 1)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gba5e6ad82'
|
|
@@ -19,7 +19,7 @@ query getFlashAppByName($flashAppName: String!) {
|
|
|
19
19
|
name
|
|
20
20
|
state
|
|
21
21
|
activeBuildId
|
|
22
|
-
endpoints { id name }
|
|
22
|
+
endpoints { id name template { name } }
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -60,7 +60,7 @@ query getFlashEnvironmentByName($input: FlashEnvironmentByNameInput!) {
|
|
|
60
60
|
state
|
|
61
61
|
activeBuildId
|
|
62
62
|
createdAt
|
|
63
|
-
endpoints { id name }
|
|
63
|
+
endpoints { id name template { name } }
|
|
64
64
|
networkVolumes { id name }
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -4,7 +4,11 @@ import asyncio
|
|
|
4
4
|
import base64
|
|
5
5
|
import datetime as dt
|
|
6
6
|
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
7
9
|
import secrets
|
|
10
|
+
import tempfile
|
|
11
|
+
from pathlib import Path
|
|
8
12
|
from typing import Any, Callable, Dict, Optional
|
|
9
13
|
from urllib.parse import quote
|
|
10
14
|
from uuid import uuid4
|
|
@@ -89,8 +93,59 @@ async def _post_authentication_attempt(
|
|
|
89
93
|
return payload
|
|
90
94
|
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
def pending_agent_login_path(profile: str) -> Path:
|
|
97
|
+
from runpod.cli.groups.config.functions import CREDENTIAL_FILE
|
|
98
|
+
|
|
99
|
+
profile_id = hashlib.sha256(profile.encode("utf-8")).hexdigest()
|
|
100
|
+
return Path(CREDENTIAL_FILE).parent / f"login-{profile_id}.json"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def save_agent_login(attempt: dict, profile: str) -> None:
|
|
104
|
+
path = pending_agent_login_path(profile)
|
|
105
|
+
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
106
|
+
fd, temporary = tempfile.mkstemp(dir=path.parent, suffix=".json")
|
|
107
|
+
try:
|
|
108
|
+
with os.fdopen(fd, "w", encoding="utf-8") as pending:
|
|
109
|
+
json.dump(attempt, pending)
|
|
110
|
+
os.replace(temporary, path)
|
|
111
|
+
except BaseException:
|
|
112
|
+
os.unlink(temporary)
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load_agent_login(profile: str) -> dict:
|
|
117
|
+
try:
|
|
118
|
+
attempt = json.loads(
|
|
119
|
+
pending_agent_login_path(profile).read_text(encoding="utf-8")
|
|
120
|
+
)
|
|
121
|
+
except FileNotFoundError:
|
|
122
|
+
raise LoginError(
|
|
123
|
+
"no pending login for this profile. Run runpod login --agent --email ADDRESS"
|
|
124
|
+
) from None
|
|
125
|
+
except ValueError:
|
|
126
|
+
raise LoginError(
|
|
127
|
+
"invalid pending login. Run runpod login --agent --email ADDRESS to request a new code"
|
|
128
|
+
) from None
|
|
129
|
+
if not isinstance(attempt, dict) or any(
|
|
130
|
+
not isinstance(attempt.get(field), str) or not attempt[field]
|
|
131
|
+
for field in ("attempt_id", "verifier", "api_url")
|
|
132
|
+
):
|
|
133
|
+
raise LoginError(
|
|
134
|
+
"invalid pending login. Run runpod login --agent --email ADDRESS to request a new code"
|
|
135
|
+
)
|
|
136
|
+
return attempt
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _agent_session() -> aiohttp.ClientSession:
|
|
140
|
+
return aiohttp.ClientSession(
|
|
141
|
+
timeout=aiohttp.ClientTimeout(total=30),
|
|
142
|
+
headers={"Accept": "application/json", "User-Agent": USER_AGENT},
|
|
143
|
+
cookie_jar=aiohttp.DummyCookieJar(),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
async def start_agent_login(email: str) -> dict:
|
|
148
|
+
"""request an emailed code and return its proof-of-possession state."""
|
|
94
149
|
verifier = secrets.token_urlsafe(32)
|
|
95
150
|
challenge = (
|
|
96
151
|
base64.urlsafe_b64encode(hashlib.sha256(verifier.encode("ascii")).digest())
|
|
@@ -99,11 +154,7 @@ async def agent_login(email: str, *, read_code: Callable[[], str]) -> str:
|
|
|
99
154
|
)
|
|
100
155
|
path = "/v2/signup/authentication-attempts"
|
|
101
156
|
|
|
102
|
-
async with
|
|
103
|
-
timeout=aiohttp.ClientTimeout(total=30),
|
|
104
|
-
headers={"Accept": "application/json", "User-Agent": USER_AGENT},
|
|
105
|
-
cookie_jar=aiohttp.DummyCookieJar(),
|
|
106
|
-
) as session:
|
|
157
|
+
async with _agent_session() as session:
|
|
107
158
|
attempt = await _post_authentication_attempt(
|
|
108
159
|
session,
|
|
109
160
|
path,
|
|
@@ -114,17 +165,20 @@ async def agent_login(email: str, *, read_code: Callable[[], str]) -> str:
|
|
|
114
165
|
if not isinstance(attempt_id, str) or not attempt_id:
|
|
115
166
|
raise LoginError("auth attempt failed to initialize")
|
|
116
167
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
168
|
+
return {"attempt_id": attempt_id, "verifier": verifier, "api_url": _build_url(path)}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def verify_agent_login(attempt: dict, code: str) -> str:
|
|
172
|
+
"""exchange a code and its saved verifier for an api key."""
|
|
173
|
+
path = "/v2/signup/authentication-attempts"
|
|
174
|
+
if attempt["api_url"] != _build_url(path):
|
|
175
|
+
raise LoginError("pending login belongs to a different API. Request a new code")
|
|
176
|
+
async with _agent_session() as session:
|
|
177
|
+
result = await _post_authentication_attempt(
|
|
178
|
+
session,
|
|
179
|
+
f"{path}/{quote(attempt['attempt_id'], safe='')}/verify",
|
|
180
|
+
{"code": code, "codeVerifier": attempt["verifier"]},
|
|
181
|
+
)
|
|
128
182
|
credential = result.get("credential")
|
|
129
183
|
api_key = credential.get("apiKey") if isinstance(credential, dict) else None
|
|
130
184
|
if not isinstance(api_key, str) or not api_key.strip():
|
|
@@ -132,6 +186,17 @@ async def agent_login(email: str, *, read_code: Callable[[], str]) -> str:
|
|
|
132
186
|
return api_key
|
|
133
187
|
|
|
134
188
|
|
|
189
|
+
async def agent_login(email: str, *, read_code: Callable[[], str]) -> str:
|
|
190
|
+
"""exchange an emailed code for an api key bound to this login's verifier."""
|
|
191
|
+
attempt = await start_agent_login(email)
|
|
192
|
+
while True:
|
|
193
|
+
code = read_code()
|
|
194
|
+
try:
|
|
195
|
+
return await verify_agent_login(attempt, code)
|
|
196
|
+
except _VerificationCodeRejected:
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
|
|
135
200
|
def _parse_expires_at(value: Optional[str]) -> Optional[dt.datetime]:
|
|
136
201
|
if not value:
|
|
137
202
|
return None
|