diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 5cbc6326c14..97b5bb69c50 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.19.12-dev0 +current_version = 0.20.1-dev0 commit = true tag = true tag_name = {new_version} @@ -28,6 +28,8 @@ values = [bumpversion:file:webui/react/craco.config.js] +[bumpversion:file:webui/react/vite.config.ts] + [bumpversion:file:helm/charts/determined/Chart.yaml] [bumpversion:glob:model_hub/examples/huggingface/*/*.yaml] diff --git a/.circleci/config.yml b/.circleci/config.yml index a4671c4ac4b..419f5b03bbc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ executors: parameters: det-version: type: string - default: 0.19.12-dev0 + default: 0.20.1-dev0 docker-image: type: string default: determinedai/cimg-base:latest @@ -182,11 +182,11 @@ commands: - when: condition: <> steps: - - run: docker pull determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-ad0591c + - run: docker pull determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-7aa5364 - when: condition: <> steps: - - run: docker pull determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c + - run: docker pull determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364 login-docker: parameters: @@ -214,7 +214,7 @@ commands: install-protoc: steps: - - run: curl -o /tmp/protoc.zip -L https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protoc-3.17.1-linux-x86_64.zip + - run: curl --retry-connrefused --retry 10 -o /tmp/protoc.zip -L https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protoc-3.17.1-linux-x86_64.zip - run: unzip -o /tmp/protoc.zip -d $HOME/.local setup-go-intg-deps: @@ -531,7 +531,7 @@ commands: default: "m5.large" compute-agent-instance-type: type: string - default: "p2.xlarge" + default: "g4dn.xlarge" max-dynamic-agents: type: integer default: 1 @@ -629,7 +629,7 @@ commands: type: string compute-agent-instance-type: type: string - default: "p2.xlarge" + default: "g4dn.xlarge" aux-agent-instance-type: type: string default: "m5.large" @@ -744,7 +744,14 @@ commands: google-project-id: <> - run: command: | - gcloud components install gke-gcloud-auth-plugin --quiet + tries=5 + until gcloud components install gke-gcloud-auth-plugin --quiet; do + if [[ $((--tries)) -eq 0 ]]; then + exit 1 + fi + sleep 15 + done + echo "export USE_GKE_GCLOUD_AUTH_PLUGIN=True" >> $BASH_ENV name: Install GKE auth plugin - run: @@ -794,7 +801,21 @@ commands: condition: <> steps: - run: - command: gcloud container node-pools create accel --cluster ${CLUSTER_ID} --region <> --num-nodes <> --accelerator type=<>,count=<> --machine-type=<> --scopes cloud-platform --node-taints=<> + command: | + gcloud container node-pools create accel \ + --cluster ${CLUSTER_ID} \ + --region '<>' \ + --num-nodes '<>' \ + --accelerator 'type=<>,count=<>' \ + --machine-type='<>' \ + --scopes cloud-platform \ + --node-taints='<>' \ + || ( + curl "$SLACK_WEBHOOK" \ + -H 'Content-Type: application/json' \ + -d "{\"text\":\"GKE node pool creation failed! $CIRCLE_BUILD_URL\"}" + circleci-agent step halt + ) name: Create GPU node pool - unless: condition: <> @@ -1210,14 +1231,18 @@ jobs: check-ts-bindings: docker: - - image: cimg/openjdk:14.0.2 + - image: <> steps: - checkout - skip-if-docs-only + - setup-python-venv: + install-python: false + extra-requirements-file: "bindings/requirements.txt" + executor: <> - attach_workspace: at: . - - run: make -C bindings get-deps - - run: make -C bindings check/typescript-fetch + - run: make -C bindings force-gen + - run: make -C bindings check/typescript check-py-bindings: docker: @@ -1228,6 +1253,7 @@ jobs: - skip-if-webui-only - setup-python-venv: install-python: false + extra-requirements-file: "bindings/requirements.txt" executor: <> - attach_workspace: at: . @@ -1775,7 +1801,7 @@ jobs: parameters: compute-agent-instance-type: type: string - default: "p2.xlarge" + default: "g4dn.xlarge" aux-agent-instance-type: type: string default: "m5.large" @@ -1826,7 +1852,7 @@ jobs: type: string compute-agent-instance-type: type: string - default: p2.xlarge + default: g4dn.xlarge aux-agent-instance-type: type: string default: m5.large @@ -1943,7 +1969,7 @@ jobs: type: string default: "1" environment-image: - default: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 + default: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 type: string accel-node-taints: type: string @@ -1963,6 +1989,11 @@ jobs: determined: true extra-requirements-file: "e2e_tests/tests/requirements.txt" executor: <> + - queue/until_front_of_line: + only-on-branch: master + # Basically wait forever -- we would prefer not to fail tests, and + # we'll likely never be this backed up. + time: "10000" - setup-gke-cluster: cluster-id: <>-$(git rev-parse --short HEAD)-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX} labels: test-mark=<> @@ -2076,7 +2107,7 @@ jobs: type: string compute-agent-instance-type: type: string - default: p2.xlarge + default: g4dn.xlarge aux-agent-instance-type: type: string default: m5.large @@ -2363,7 +2394,7 @@ workflows: matrix: parameters: parallelism: [2] - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] cluster-id-prefix: ["parallel"] mark: ["parallel"] @@ -2415,6 +2446,7 @@ workflows: parallelism: [1] slack-mentions: ["${SLACK_USER_ID}"] machine-type: ["n1-standard-32"] + gpu-type: ["nvidia-tesla-t4"] gpus-per-machine: [4] num-machines: [2] @@ -2615,6 +2647,7 @@ workflows: parallelism: [1] slack-mentions: ["${SLACK_USER_ID}"] machine-type: ["n1-standard-32"] + gpu-type: ["nvidia-tesla-t4"] gpus-per-machine: [4] num-machines: [2] @@ -2735,7 +2768,6 @@ workflows: parallelism: [2] cluster-id-prefix: ["nightly"] mark: ["nightly"] - compute-agent-instance-type: ["g4dn.4xlarge"] # GPU tests - request-gpu-tests: @@ -2808,7 +2840,7 @@ workflows: - package-and-push-system-dev matrix: parameters: - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] cluster-id-prefix: ["transformers"] mark: ["model_hub_transformers"] @@ -2824,7 +2856,7 @@ workflows: - package-and-push-system-dev matrix: parameters: - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] cluster-id-prefix: ["transformers-amp"] mark: ["model_hub_transformers_amp"] @@ -2875,7 +2907,6 @@ workflows: parallelism: [2] cluster-id-prefix: ["nightly"] mark: ["nightly"] - compute-agent-instance-type: ["g4dn.4xlarge"] - test-e2e-aws: name: test-e2e-gpu-distributed context: aws @@ -2883,7 +2914,7 @@ workflows: parameters: cluster-id-prefix: ["distributed"] mark: ["distributed"] - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] max-dynamic-agents: [2] - test-e2e-aws: @@ -2893,7 +2924,7 @@ workflows: parameters: cluster-id-prefix: ["transformers"] mark: ["model_hub_transformers"] - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] max-dynamic-agents: [2] - test-e2e-aws: @@ -2903,7 +2934,7 @@ workflows: parameters: cluster-id-prefix: ["transformers-amp"] mark: ["model_hub_transformers_amp"] - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] max-dynamic-agents: [2] - test-e2e-aws: @@ -2913,7 +2944,7 @@ workflows: parameters: cluster-id-prefix: ["mmdetection"] mark: ["model_hub_mmdetection"] - compute-agent-instance-type: ["p2.8xlarge"] + compute-agent-instance-type: ["g4dn.metal"] aux-agent-instance-type: ["m5.large"] max-dynamic-agents: [2] # DeepSpeed tests do not work on K80s, so we need V100 instances. diff --git a/.gitattributes b/.gitattributes index 76bc7368e10..8b2e54bd4a7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,8 @@ # ignore generated code for cli diffs and merges. treat as binary. # ignore generated code for GitHub UIs. proto/pkg/**/* -diff -merge linguist-generated=true -master/pkg/schemas/**/* -diff -merge linguist-generated=true +master/pkg/schemas/expconf/zgen_* -diff -merge linguist-generated=true webui/react/src/services/api-ts-sdk/**/* -diff -merge linguist-generated=true harness/determined/common/api/bindings.py -diff -merge linguist-generated=true +docs/swagger-ui/swagger-ui*js* -diff -merge +docs/swagger-ui/swagger-ui-main* diff merge diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43c1d408f76..31385242ba1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,12 +51,11 @@ git clone --recurse-submodules https://github.com/determined-ai/determined.git - python3-venv - python3-wheel - python3-dev -- Node (>= 16.13, < 17) +- Node (>= 16.13, < 20) - NPM (>= 8) - Docker (>= 19.03) - Helm (>= 3.0.0) - protobuf-compiler (>= 3.15) -- Java (>= 7) - cURL (>= 7) - jq (>= 1.6) - socat (>= 1.7) @@ -96,13 +95,13 @@ eval "$($HOME/.linuxbrew/bin/brew shellenv)" Install the Determined prerequisites: ```sh -brew install go@1.18 python@3.7 node@16 openjdk@11 protobuf docker helm curl jq socat +brew install go@1.18 python@3.7 node@16 protobuf docker helm curl jq socat ``` -Add Python, Node, and openJDK to your PATH: +Add Python and Node to your PATH: ```sh -echo 'export PATH="$HOME/.linuxbrew/opt/python@3.7/bin:$HOME/.linuxbrew/opt/node@16/bin:$HOME/.linuxbrew/opt/openjdk@11/bin:$PATH"' >> $HOME/.profile +echo 'export PATH="$HOME/.linuxbrew/opt/python@3.7/bin:$HOME/.linuxbrew/opt/node@16/bin:$PATH"' >> $HOME/.profile source $HOME/.profile ``` diff --git a/VERSION b/VERSION index 578f0d79dae..bb75f93fbbb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.19.12-dev0 \ No newline at end of file +0.20.1-dev0 \ No newline at end of file diff --git a/agent/go.mod b/agent/go.mod index 9ae7177577d..ab6e3a29cf1 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -18,7 +18,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 - golang.org/x/sys v0.2.0 + golang.org/x/sys v0.5.0 gotest.tools v2.2.0+incompatible ) @@ -106,9 +106,9 @@ require ( github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.10.0 // indirect + github.com/prometheus/client_golang v1.11.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.25.0 // indirect + github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/santhosh-tekuri/jsonschema/v2 v2.2.0 // indirect @@ -141,11 +141,11 @@ require ( go.opentelemetry.io/proto/otlp v0.12.1 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect - golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/api v0.56.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/agent/go.sum b/agent/go.sum index 785d1e66943..7fea316a663 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -221,6 +221,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -483,6 +484,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -641,8 +643,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -656,8 +659,9 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.25.0 h1:IjJYZJCI8HZYtqA3xYwGyDzSCy1r4CA2GRh+4vdOmtE= github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -944,8 +948,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1047,6 +1051,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1056,12 +1061,13 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1070,8 +1076,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/agent/internal/agent.go b/agent/internal/agent.go index 9b147726ef6..40dcbe526d6 100644 --- a/agent/internal/agent.go +++ b/agent/internal/agent.go @@ -189,6 +189,8 @@ func (a *Agent) run(ctx context.Context) error { } case msg.SignalContainer != nil: manager.SignalContainer(ctx, *msg.SignalContainer) + case msg.AgentShutdown != nil: + return errors.New(msg.AgentShutdown.ErrMsg) default: panic(fmt.Sprintf("unknown message received: %+v", msg)) } diff --git a/agent/internal/container/container.go b/agent/internal/container/container.go index d96df854660..25f267c14e3 100644 --- a/agent/internal/container/container.go +++ b/agent/internal/container/container.go @@ -176,7 +176,6 @@ func (c *Container) run(parent context.Context) (err error) { switch signal { case syscall.SIGKILL: c.log.Tracef("signal %s, canceling run-scoped context", signal) - launchgroup.Cancel() return ErrKilledBeforeRun default: c.log.Warnf("ignoring signal other than SIGKILL %s before running", signal) @@ -219,6 +218,7 @@ func (c *Container) run(parent context.Context) (err error) { if err != nil { c.log.Trace("ensuring cleanup of container (canceled prior to the monitoring loop)") if remove { + // TODO this context could be canceled between CreateContainer and this. if rErr := c.docker.RemoveContainer(ctx, dockerID, true); rErr != nil { c.log.WithError(rErr).Debug("couldn't cleanup container") } diff --git a/bindings/Makefile b/bindings/Makefile index 7d220531982..4d7de19bb04 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -1,49 +1,33 @@ SWAGGER_SPEC := ../proto/build/swagger/determined/api/v1/api.swagger.json -CODEGEN_VER = 2.4.27 -CODEGEN_BIN := swagger-codegen-cli-$(CODEGEN_VER).jar py_bindings_dest := ../harness/determined/common/api/bindings.py ts_bindings_dest := ../webui/react/src/services/api-ts-sdk -py_generator := generate_bindings.py +py_generator := generate_bindings_py.py +ts_generator := generate_bindings_ts.py .PHONY: all -all: get-deps - $(MAKE) build - -.PHONY: get-deps -get-deps: deps/$(CODEGEN_BIN) - -deps/$(CODEGEN_BIN): - mkdir -p deps - curl https://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/$(CODEGEN_VER)/$(CODEGEN_BIN) \ - -o deps/$(CODEGEN_BIN) +all: build .PHONY: force-gen force-gen: touch $(SWAGGER_SPEC) -.PHONY: bindings-copy-over -bindings-copy-over: - rm -rf $(ts_bindings_dest) - cp -r build/typescript-fetch $(ts_bindings_dest) - -$(py_bindings_dest): $(SWAGGER_SPEC) $(py_generator) +$(py_bindings_dest): $(SWAGGER_SPEC) swagger_parser.py $(py_generator) python $(py_generator) --output $(py_bindings_dest) +$(ts_bindings_dest): $(SWAGGER_SPEC) swagger_parser.py $(ts_generator) $(wildcard static_ts_files/*) + python $(ts_generator) --output $@ + .PHONY: build/python build/python: $(py_bindings_dest) -build/%: $(SWAGGER_SPEC) deps/$(CODEGEN_BIN) - mkdir -p build/$* - java -jar deps/$(CODEGEN_BIN) generate -i $(SWAGGER_SPEC) \ - -l typescript-fetch -o build/typescript-fetch - $(MAKE) bindings-copy-over - touch build/typescript-fetch +.PHONY: build/typescript +build/typescript: $(ts_bindings_dest) .PHONY: build -build: build/typescript-fetch $(py_bindings_dest) +build: $(ts_bindings_dest) $(py_bindings_dest) .PHONY: check -check: force-gen check/python check/typescript-fetch +check: force-gen check/python check/typescript # Checking that the (committed) generated code is up-to-date by ensuring that # git reports the files as unchanged after forcibly regenerating them. # WARNING this has a dependency on proto module being built recently @@ -53,17 +37,13 @@ check: force-gen check/python check/typescript-fetch check/python: build/python test -z "$(shell git status --porcelain $(py_bindings_dest))" || (git diff; false) -.PHONY: check-typescript-fetch -check/typescript-fetch: build/typescript-fetch +.PHONY: check-typescript +check/typescript: build/typescript test -z "$(shell git status --porcelain $(ts_bindings_dest)/*)" || (git diff; false) -.PHONY: clean-deps -clean-deps: - rm -rf deps/ - .PHONY: clean clean: rm -rf build/ $(py_bindings_dest) $(ts_bindings_dest) .PHONY: clean-all -clean-all: clean clean-deps +clean-all: clean diff --git a/bindings/generate_bindings.py b/bindings/generate_bindings.py deleted file mode 100644 index 62015f30ef8..00000000000 --- a/bindings/generate_bindings.py +++ /dev/null @@ -1,838 +0,0 @@ -import abc -import json -import os -import sys -import typing - -SWAGGER = "proto/build/swagger/determined/api/v1/api.swagger.json" -SWAGGER = os.path.join(os.path.dirname(__file__), "..", SWAGGER) - -Code = str -TypeDefs = typing.Dict[str, typing.Optional["TypeDef"]] - - -class TypeAnno: - def annotation(self, prequoted=False) -> Code: - raise NotImplementedError(type(self)) - - def need_parse(self) -> bool: - raise NotImplementedError(type(self)) - - def load(self, val: Code) -> Code: - raise NotImplementedError(type(self)) - - def dump(self, val: Code, omit_unset: Code) -> Code: - raise NotImplementedError(type(self)) - - def isnone(self) -> bool: - # Only Refs to empty structs ever return True; we skip generating them. - return False - - def need_urlparam_dump(self) -> bool: - """ - Dump a value for url parameters. - - Defaults to need_parse(), since dump_as_urlparam() defaults to dump() - """ - return self.need_parse() - - def dump_as_urlparam(self, val: Code) -> Code: - """ - Dump a value for url parameters. - - Defaults to the normal dump(), but can be overridden. - """ - return self.dump(val, omit_unset="True") - - -class TypeDef: - def gen_def(self) -> Code: - raise NotImplementedError(type(self)) - - -class NoParse: - """A compositional class for things where json.loads/dumps is sufficient.""" - - def need_parse(self) -> bool: - return False - - def load(self, val: Code) -> Code: - return val - - def dump(self, val: Code, omit_unset: Code) -> Code: - return val - - -class Any(NoParse, TypeAnno): - def __repr__(self): - return "Any" - - def annotation(self, prequoted=False) -> Code: - return "typing.Any" - - -class String(NoParse, TypeAnno): - def __init__(self): - pass - - def __repr__(self): - return "str" - - def annotation(self, prequoted=False) -> Code: - return "str" - - -class Float(TypeAnno): - def __init__(self): - pass - - def __repr__(self): - return "float" - - def annotation(self, prequoted=False) -> Code: - return "float" - - def need_parse(self) -> bool: - return True - - def load(self, val: Code) -> Code: - return f"float({val})" - - def dump(self, val: Code, omit_unset: Code) -> Code: - return f"dump_float({val})" - - -class Int(NoParse, TypeAnno): - def __init__(self): - pass - - def __repr__(self): - return "int" - - def annotation(self, prequoted=False) -> Code: - return "int" - - -class Bool(NoParse, TypeAnno): - def __init__(self): - pass - - def __repr__(self): - return "bool" - - def annotation(self, prequoted=False) -> Code: - return "bool" - - def need_urlparam_dump(self) -> bool: - return True - - def dump_as_urlparam(self, val: Code) -> Code: - """ - Covert True to "true" and False to "false", but only to embed in a url parameter. - - By default, requests encodes True as `val=True`. GRPC pukes unless you encode `val=true`. - """ - return f"str({val}).lower()" - - -class Ref(TypeAnno): - # Collect refs as we instantiate them, for the linking step. - all_refs = [] - - def __init__(self, name: str, url_encodable=False): - self.name = name - self.linked = False - self.defn = None - self.url_encodable = url_encodable - Ref.all_refs.append(self) - - def __repr__(self): - return self.name - - def annotation(self, prequoted=False) -> Code: - assert self.linked, "link step must be completed before generating code!" - if not self.defn: - return "None" - out = self.name - if not prequoted: - return f'"{out}"' - return out - - def need_parse(self) -> bool: - assert self.linked, "link step must be completed before generating code!" - return True - - def isnone(self) -> bool: - assert self.linked, "link step must be completed before generating code!" - return self.defn is None - - def load(self, val: Code) -> Code: - assert self.linked, "link step must be completed before generating code!" - assert self.defn, "it doesn't make sense to load an empty class" - assert isinstance(self.defn, (Enum, Class)), ( - self.name, - type(self.defn).__name__, - ) - return self.defn.load(val) - - def dump(self, val: Code, omit_unset: Code) -> Code: - assert self.linked, "link step must be completed before generating code!" - assert self.defn, "it doesn't make sense to dump an empty class" - assert isinstance(self.defn, (Enum, Class)), ( - self.name, - type(self.defn).__name__, - ) - return self.defn.dump(val, omit_unset) - - -class Dict(TypeAnno): - def __init__(self, values: TypeAnno): - self.values = values - - def __repr__(self): - return f"Dict[str, {self.values}]" - - def annotation(self, prequoted=False) -> Code: - out = f"typing.Dict[str, {self.values.annotation(True)}]" - if not prequoted: - return f'"{out}"' - return out - - def need_parse(self) -> bool: - return self.values.need_parse() - - def load(self, val: Code) -> Code: - if not self.need_parse(): - return val - return f"{{k: {self.values.load('v')} for k, v in {val}.items()}}" - - def dump(self, val: Code, omit_unset: Code) -> Code: - if not self.need_parse(): - return val - each = self.values.dump("v", omit_unset) - return f"{{k: {each} for k, v in {val}.items()}}" - - -class Sequence(TypeAnno): - def __init__(self, items): - self.items = items - - def __repr__(self): - return f"Sequence[{self.items}]" - - def annotation(self, prequoted=False) -> Code: - out = f"typing.Sequence[{self.items.annotation(True)}]" - if not prequoted: - return f'"{out}"' - return out - - def need_parse(self) -> bool: - return self.items.need_parse() - - def load(self, val: Code) -> Code: - if not self.need_parse(): - return val - return f"[{self.items.load('x')} for x in {val}]" - - def dump(self, val: Code, omit_unset: Code) -> Code: - if not self.need_parse(): - return val - each = self.items.dump("x", omit_unset) - return f"[{each} for x in {val}]" - - -class Parameter: - def __init__( - self, - name: str, - typ: TypeAnno, - required: bool, - where: str, - serialized_name: str = None, - ) -> None: - self.name = name - self.serialized_name = serialized_name - self.type = typ - self.required = required - self.where = where - # validations - assert where in ("query", "body", "path", "definitions"), (name, where) - assert where != "path" or required, name - if where == "path": - if not isinstance(typ, (String, Int, Bool)): - raise AssertionError(f"bad type in path parameter {name}: {typ}") - if where == "query": - underlying_typ = typ.items if isinstance(typ, Sequence) else typ - if not isinstance(underlying_typ, (String, Int, Bool)): - if not (isinstance(underlying_typ, Ref) and underlying_typ.url_encodable): - raise AssertionError(f"bad type in query parameter {name}: {typ}") - - def gen_function_param(self) -> Code: - if self.required: - typestr = self.type.annotation() - default = "" - else: - typestr = f'"typing.Optional[{self.type.annotation(prequoted=True)}]"' - default = " = None" - return f" {self.name}: {typestr}{default}," - - def gen_init_param(self) -> Code: - if self.required: - typestr = self.type.annotation() - default = "" - else: - typestr = f'"typing.Union[{self.type.annotation(prequoted=True)}, None, Unset]"' - default = " = _unset" - return f" {self.name}: {typestr}{default}," - - def dump(self, omit_unset: Code) -> Code: - return self.type.dump(self.name, omit_unset) - - -class Class(TypeDef): - def __init__(self, name: str, params: typing.Dict[str, Parameter]): - self.name = name - self.params = params - - def load(self, val: Code) -> Code: - return f"{self.name}.from_json({val})" - - def dump(self, val: Code, omit_unset: Code) -> Code: - return f"{val}.to_json({omit_unset})" - - def gen_def(self) -> Code: - required = sorted(p for p in self.params if self.params[p].required) - optional = sorted(p for p in self.params if not self.params[p].required) - out = [f"class {self.name}:"] - for k in optional: - v = self.params[k] - out += [f' {k}: "typing.Optional[{v.type.annotation(prequoted=True)}]" = None'] - out += [""] - out += [" def __init__("] - out += [" self,"] - out += [" *,"] - for k in required + optional: - v = self.params[k] - out += [" " + v.gen_init_param()] - out += [" ):"] - for k in required: - out += [f" self.{k} = {k}"] - for k in optional: - out += [f" if not isinstance({k}, Unset):"] - out += [f" self.{k} = {k}"] - out += [""] - out += [" @classmethod"] - out += [f' def from_json(cls, obj: Json) -> "{self.name}":'] - out += [' kwargs: "typing.Dict[str, typing.Any]" = {'] - for k in required: - v = self.params[k] - if v.type.need_parse(): - parsed = v.type.load(f'obj["{k}"]') - else: - parsed = f'obj["{k}"]' - out += [f' "{k}": {parsed},'] - out += [" }"] - for k in optional: - v = self.params[k] - if v.type.need_parse(): - parsed = v.type.load(f'obj["{k}"]') - parsed = parsed + f' if obj["{k}"] is not None else None' - else: - parsed = f'obj["{k}"]' - out += [f' if "{k}" in obj:'] - out += [f' kwargs["{k}"] = {parsed}'] - out += [" return cls(**kwargs)"] - out += [""] - out += [" def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]:"] - out += [' out: "typing.Dict[str, typing.Any]" = {'] - for k in required: - v = self.params[k] - if v.type.need_parse(): - parsed = v.type.dump(f"self.{k}", "omit_unset") - else: - parsed = f"self.{k}" - out.append(f' "{k}": {parsed},') - out += [" }"] - for k in optional: - v = self.params[k] - if v.type.need_parse(): - parsed = v.type.dump(f"self.{k}", "omit_unset") - parsed = f"None if self.{k} is None else {parsed}" - else: - parsed = f"self.{k}" - out += [f' if not omit_unset or "{k}" in vars(self):'] - out += [f' out["{k}"] = {parsed}'] - out += [" return out"] - - return "\n".join(out) - - -class Enum(TypeDef): - def __init__(self, name, members): - self.name = name - self.members = members - - def load(self, val: Code) -> Code: - return f"{self.name}({val})" - - def dump(self, val: Code, omit_unset: Code) -> Code: - return f"{val}.value" - - def gen_def(self) -> Code: - out = [f"class {self.name}(enum.Enum):"] - out += [f' {v} = "{v}"' for v in self.members] - return "\n".join(out) - - -class Function: - def __init__( - self, - name: str, - method: str, - path: str, - params: typing.Dict[str, Parameter], - responses: typing.Dict[str, dict], - streaming: bool, - ): - self.name = name - self.method = method - self.path = path - self.params = params - self.responses = responses - self.streaming = streaming - - def __repr__(self) -> str: - out = ( - f"Function({self.name}):\n" - f" self.method = {self.method.upper()}\n" - f" self.params = {self.params}\n" - f" responses = {{" - ) - for code, resp in self.responses.items(): - out += f"\n {code} = {resp}" - out += "\n }" - return out - - def gen_def(self) -> Code: - # Function name. - out = [f"def {self.method}_{self.name}("] - - # Function parameters. - out += [' session: "api.Session",'] - if self.params: - out += [" *,"] - - required = sorted(p for p in self.params if self.params[p].required) - optional = sorted(p for p in self.params if not self.params[p].required) - for name in required + optional: - out += [self.params[name].gen_function_param()] - - # Function return type. - # We wrap the return type annotation for streaming or union responses. - need_quotes = self.streaming or len(self.responses) > 1 - returntypes = set(r.annotation(prequoted=need_quotes) for r in self.responses.values()) - returntypestr = ",".join(sorted(returntypes)) - if len(returntypes) > 1: - returntypestr = f"typing.Union[{returntypestr}]" - if self.streaming: - returntypestr = f"typing.Iterable[{returntypestr}]" - if need_quotes: - returntypestr = f'"{returntypestr}"' - - out += [f") -> {returntypestr}:"] - - # Function body. - path_params = sorted(p for p in self.params if self.params[p].where == "path") - body_params = sorted(p for p in self.params if self.params[p].where == "body") - query_params = sorted(p for p in self.params if self.params[p].where == "query") - - pathstr = f'"{self.path}"' - if path_params: - # Happily, we can just generate an f-string based on the path swagger gives us. - pathstr = "f" + pathstr - - if query_params: - out += [" _params = {"] - for p in query_params: - param = self.params[p] - if param.type.need_urlparam_dump(): - value = f"{param.type.dump_as_urlparam(param.name)}" - if not param.required: - value += f" if {param.name} is not None else None" - else: - value = param.name - out += [f' "{self.params[p].serialized_name}": {value},'] - out += [" }"] - else: - out += [" _params = None"] - - if "body" in self.params: - # It is important that request bodies omit unset values so that PATCH request bodies - # do not include extraneous None values. - bodystr = self.params["body"].dump("True") - else: - bodystr = "None" - out += [" _resp = session._do_request("] - out += [f' method="{self.method.upper()}",'] - out += [f" path={pathstr},"] - out += [" params=_params,"] - out += [f" json={bodystr},"] - out += [" data=None,"] - out += [" headers=None,"] - out += [" timeout=None,"] - out += [f" stream={self.streaming},"] - out += [" )"] - for expect, returntype in self.responses.items(): - out += [f" if _resp.status_code == {expect}:"] - if not self.streaming: - if returntype.isnone(): - out += [" return"] - else: - out += [f' return {returntype.load("_resp.json()")}'] - else: - assert not returntype.isnone(), "unable to stream empty result class: {self}" - # Too many quotes to do bit inline: - yieldable = returntype.load('_j["result"]') - out += [ - f" for _line in _resp.iter_lines():", - f" _j = json.loads(_line)", - f' if "error" in _j:', - f" raise APIHttpStreamError(", - f' "{self.method}_{self.name}",', - f' runtimeStreamError.from_json(_j["error"])', - f" )", - f" yield {yieldable}", - f" return", - ] - out += [f' raise APIHttpError("{self.method}_{self.name}", _resp)'] - - return "\n".join(out) - - -def classify_type(enums: dict, path: str, schema: dict) -> TypeAnno: - # enforce valid jsonschema: - assert isinstance(schema, dict), (path, schema) - if "enum" in schema: - name = enums[json.dumps(schema["enum"])] - assert name, (name, schema) - return Ref(name, url_encodable=True) - - if "$ref" in schema: - ref = schema["$ref"] - start = "#/definitions/" - assert ref.startswith(start), ref - return Ref(ref[len(start) :]) - - if "type" not in schema: - # When "type" is not present, any json element should be valid. - return Any() - - # only $refs don't have types - assert "type" in schema, (path, schema) - - if schema["type"] == "string": - return String() - - if schema["type"] == "integer": - return Int() - - if schema["type"] == "boolean": - return Bool() - - if schema["type"] in ("float", "number"): - return Float() - - if schema["type"] == "object" and "properties" not in schema: - adlProps = schema.get("additionalProperties") - if adlProps is None: - return Dict(Any()) - return Dict(classify_type(enums, path + ".additionalProperties", adlProps)) - - if schema["type"] == "array": - items = schema.get("items") - if items is None: - raise ValueError(path, schema) - return Sequence(classify_type(enums, path + ".items", items)) - - raise ValueError(f"unhandled schema: {schema} @ {path}") - - -def process_enums(swagger_definitions: dict) -> typing.Dict[int, str]: - """ - Process enums from swagger definitions. In OpenAPI spec v2 generated - by protoc-gen-openapi enums are not linked to a definition and are inlined. - Here we preprocess them so that they can be linked to a definition. - """ - enums = {} - for name, schema in swagger_definitions.items(): - if "enum" in schema: - members = schema["enum"] - if enums.get(json.dumps(members)) is not None: - print( - "ambiguous enum parameter:", - name, - members, - file=sys.stderr, - ) - enums[json.dumps(members)] = name - return enums - - -def process_definitions(swagger_definitions: dict, enums: dict) -> TypeDefs: - defs = {} # type: TypeDefs - for name, schema in swagger_definitions.items(): - path = name - if "enum" in schema: - if schema["type"] == "string": - members = schema["enum"] - defs[name] = Enum(name, members) - continue - raise ValueError("unhandled enum type ({schema['type']}): {schema}") - - if schema["type"] == "object": - # top-level named objects should be classes, not typed dictionaries: - assert "additionalProperties" not in schema, (name, schema) - if "properties" in schema: - required = set(schema.get("required", [])) - members = { - k: Parameter( - k, classify_type(enums, f"{path}.{k}", v), (k in required), "definitions" - ) - for k, v in schema["properties"].items() - } - defs[name] = Class(name, members) - continue - else: - # empty responses or empty requests... we don't care. - defs[name] = None - continue - raise ValueError(f"unhandled schema: {schema} @ {path}") - return defs - - -def is_expected_path(text: str) -> bool: - """ - Check if any dots appear outside of curly braces, if any. - This is assuming there are no nested curly braces. - """ - in_braces = False - for c in text: - if c == "{": - in_braces = True - elif c == "}": - in_braces = False - elif c == "." and not in_braces: - return False - return True - - -def process_paths(swagger_paths: dict, enums: dict) -> typing.Dict[str, Function]: - ops = {} - for path, methods in swagger_paths.items(): - for method, spec in methods.items(): - name = spec["operationId"] - # Figure out response types. - responses = {} - streaming = None - bad_op = False - for code, rspec in spec["responses"].items(): - rschema = rspec["schema"] - if code == "default": - # We expect all "default" responses to be runtimeErrors, and we ignore them. - default_type = classify_type(enums, f"{name}.responses.default", rschema) - assert isinstance(default_type, Ref), rschema - assert default_type.name == "runtimeError", rschema - # Safe to ignore this return type. - continue - - if rschema.get("type") == "": - # not a valid response schema, skipping - bad_op = True - break - - if rspec.get("schema", {}).get("title", "").startswith("Stream result"): - # We expect a specific structure to streaming endpoints. - assert rschema["type"] == "object", rschema - assert "additionalProperties" not in rschema, rschema - rprops = rschema["properties"] - assert set(rprops.keys()) == set(("result", "error")), rschema - result_type = classify_type( - enums, f"{name}.responses.{code}.properties.result", rprops["result"] - ) - error_type = classify_type( - enums, f"{name}.responses.{code}.properties.error", rprops["error"] - ) - # We expect all "error" results to be runtimeStreamError. They are parsed in - # code generated by Function.gen_def(). - assert isinstance(error_type, Ref), rschema - assert error_type.name == "runtimeStreamError", rschema - if streaming is False: - raise ValueError( - f"a method must be either all-streaming or all-nonstreaming: {rspec}" - ) - streaming = True - responses[code] = result_type - continue - - responses[code] = classify_type(enums, f"{name}.responses.{code}", rschema) - if streaming is True: - raise ValueError( - f"a method must be either all-streaming or all-nonstreaming: {rspec}" - ) - streaming = False - - if bad_op: - continue - - assert streaming is not None - - # Figure out parameters. - params = {} - for pspec in spec.get("parameters", []): - where = pspec["in"] - serialized_name = None - if where == "query": # preserve query parameter names - serialized_name = pname = pspec["name"] - pname = pspec["name"].replace(".", "_") - required = pspec.get("required", False) - if "schema" in pspec: - pschema = pspec["schema"] - else: - # swagger has some weird inlining going on here... - inlined = ("type", "format", "items", "properties", "enum") - pschema = {k: pspec[k] for k in inlined if k in pspec} - ptype = classify_type(enums, f"{name}.{pname}", pschema) - params[pname] = Parameter(pname, ptype, required, where, serialized_name) - - assert is_expected_path(path), (path, name) - path = path.replace(".", "_") - op = Function(name, method, path, params, responses, streaming) - ops[name] = op - return ops - - -def link_all_refs(defs: TypeDefs) -> None: - for ref in Ref.all_refs: - ref.linked = True - ref.defn = defs[ref.name] - - -def gen_paginated(defs: TypeDefs) -> typing.List[str]: - paginated = [] - for k, defn in defs.items(): - defn = defs[k] - if defn is None or not isinstance(defn, Class): - continue - # Note that our goal is to mimic duck typing, so we only care if the "pagination" attribute - # exists with a v1Pagination type. - if any(n == "pagination" and p.type.name == "v1Pagination" for n, p in defn.params.items()): - paginated.append(defn.name) - - if not paginated: - return [] - - out = [] - out += ["# Paginated is a union type of objects whose .pagination"] - out += ["# attribute is a v1Pagination-type object."] - out += ["Paginated = typing.Union["] - out += [f" {name}," for name in sorted(paginated)] - out += ["]"] - return out - - -def pybindings(swagger: dict) -> str: - prefix = """ -# Code generated by generate_bindings.py. DO NOT EDIT. -import enum -import json -import math -import typing - -import requests - -if typing.TYPE_CHECKING: - from determined.common import api - -# flake8: noqa -Json = typing.Any - - -# Unset is a type to distinguish between things not set and things set to None. -class Unset: - pass - - -_unset = Unset() - - -def dump_float(val: typing.Any) -> typing.Any: - if math.isnan(val): - return "Nan" - if math.isinf(val): - return "Infinity" if val > 0 else "-Infinity" - return val - - -class APIHttpError(Exception): - # APIHttpError is used if an HTTP(s) API request fails. - def __init__(self, operation_name: str, response: requests.Response) -> None: - self.response = response - self.operation_name = operation_name - self.message = ( - f"API Error: {operation_name} failed: {response.reason}." - ) - - def __str__(self) -> str: - return self.message - - -class APIHttpStreamError(APIHttpError): - # APIHttpStreamError is used if an streaming API request fails mid-stream. - def __init__(self, operation_name: str, error: "runtimeStreamError") -> None: - self.operation_name = operation_name - self.error = error - self.message = ( - f"Stream Error during {operation_name}: {error.message}" - ) - - def __str__(self) -> str: - return self.message - -""".lstrip() - - out = [prefix] - - enums = process_enums(swagger["definitions"]) - defs = process_definitions(swagger["definitions"], enums) - ops = process_paths(swagger["paths"], enums) - link_all_refs(defs) - - for k in sorted(defs): - defn = defs[k] - if defn is None: - continue - out += [defn.gen_def()] - out += [""] - - for k in sorted(ops): - out += [ops[k].gen_def()] - out += [""] - - # Also generate a list of Paginated response types. - out += gen_paginated(defs) - - return "\n".join(out).strip() - - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("--output", "-o", action="store", required=True, help="output file") - args = parser.parse_args() - - with open(SWAGGER) as f: - swagger = json.load(f) - bindings = pybindings(swagger) - with open(args.output, "w") as f: - print(bindings, file=f) diff --git a/bindings/generate_bindings_py.py b/bindings/generate_bindings_py.py new file mode 100644 index 00000000000..a71f298c983 --- /dev/null +++ b/bindings/generate_bindings_py.py @@ -0,0 +1,453 @@ +import os +import swagger_parser +import typing +from typing_extensions import assert_never + +SWAGGER = "proto/build/swagger/determined/api/v1/api.swagger.json" +SWAGGER = os.path.join(os.path.dirname(__file__), "..", SWAGGER) + +Code = str +SwaggerType = typing.Union[swagger_parser.TypeAnno, swagger_parser.TypeDef] +no_parse_types = ( + swagger_parser.Any, + swagger_parser.String, + swagger_parser.DateTime, + swagger_parser.Int, + swagger_parser.Bool, +) + + +def annotation(anno: swagger_parser.TypeAnno, prequoted=False) -> Code: + if isinstance(anno, swagger_parser.Any): + return "typing.Any" + if isinstance(anno, (swagger_parser.String, swagger_parser.DateTime)): + return "str" + if isinstance(anno, swagger_parser.Float): + return "float" + if isinstance(anno, swagger_parser.Int): + return "int" + if isinstance(anno, swagger_parser.Bool): + return "bool" + if isinstance(anno, swagger_parser.Ref): + if anno.defn is None or ( + isinstance(anno.defn, swagger_parser.Class) and not anno.defn.params + ): + return "None" + out = anno.name + if not prequoted: + return f'"{out}"' + return out + if isinstance(anno, swagger_parser.Dict): + out = f"typing.Dict[str, {annotation(anno.values, True)}]" + if not prequoted: + return f'"{out}"' + return out + if isinstance(anno, swagger_parser.Sequence): + out = f"typing.Sequence[{annotation(anno.items, True)}]" + if not prequoted: + return f'"{out}"' + return out + assert_never(anno) + + +def need_parse(anno: swagger_parser.TypeAnno) -> bool: + if isinstance(anno, no_parse_types): + return False + if isinstance(anno, (swagger_parser.Float, swagger_parser.Ref)): + return True + if isinstance(anno, swagger_parser.Dict): + return need_parse(anno.values) + if isinstance(anno, swagger_parser.Sequence): + return need_parse(anno.items) + assert_never(anno) + + +def load(anno: SwaggerType, val: Code) -> Code: + if isinstance(anno, no_parse_types): + return val + if isinstance(anno, swagger_parser.Float): + return f"float({val})" + if isinstance(anno, swagger_parser.Ref): + assert anno.defn + return load(anno.defn, val) + if isinstance(anno, swagger_parser.Dict): + if not need_parse(anno): + return val + return f"{{k: {load(anno.values, 'v')} for k, v in {val}.items()}}" + if isinstance(anno, swagger_parser.Sequence): + if not need_parse(anno): + return val + return f"[{load(anno.items, 'x')} for x in {val}]" + if isinstance(anno, swagger_parser.Class): + return f"{anno.name}.from_json({val})" + if isinstance(anno, swagger_parser.Enum): + return f"{anno.name}({val})" + assert_never(anno) + + +def dump(anno: SwaggerType, val: Code, omit_unset: Code) -> Code: + if isinstance(anno, no_parse_types): + return val + if isinstance(anno, swagger_parser.Float): + return f"dump_float({val})" + if isinstance(anno, swagger_parser.Ref): + assert anno.defn + return dump(anno.defn, val, omit_unset) + if isinstance(anno, swagger_parser.Dict): + if not need_parse(anno): + return val + each = dump(anno.values, "v", omit_unset) + return f"{{k: {each} for k, v in {val}.items()}}" + if isinstance(anno, swagger_parser.Sequence): + if not need_parse(anno): + return val + each = dump(anno.items, "x", omit_unset) + return f"[{each} for x in {val}]" + if isinstance(anno, swagger_parser.Class): + return f"{val}.to_json({omit_unset})" + if isinstance(anno, swagger_parser.Enum): + return f"{val}.value" + assert_never(anno) + + +def gen_init_param(param: swagger_parser.Parameter) -> Code: + if param.required: + typestr = annotation(param.type) + default = "" + else: + typestr = f'"typing.Union[{annotation(param.type, prequoted=True)}, None, Unset]"' + default = " = _unset" + return f" {param.name}: {typestr}{default}," + + +def gen_function_param(param: swagger_parser.Parameter): + if param.required: + typestr = annotation(param.type) + default = "" + else: + typestr = f'"typing.Optional[{annotation(param.type, prequoted=True)}]"' + default = " = None" + return f" {param.name}: {typestr}{default}," + + +def is_streaming_response(defn: typing.Optional[swagger_parser.TypeDef]): + if isinstance(defn, swagger_parser.Class) and set(defn.params.keys()) == set( + ["result", "error"] + ): + error_ref = defn.params["error"] + return ( + isinstance(error_ref.type, swagger_parser.Ref) + and error_ref.type.name == "runtimeStreamError" + ) + return False + + +def unwrap_streaming_response(anno: swagger_parser.TypeAnno): + if not isinstance(anno, swagger_parser.Ref) or not is_streaming_response(anno.defn): + return anno + assert isinstance(anno.defn, swagger_parser.Class) + return anno.defn.params["result"].type + + +def gen_function(func: swagger_parser.Function) -> Code: + # Function name. + out = [f"def {func.method}_{func.name}("] + + # Function parameters. + out += [' session: "api.Session",'] + if func.params: + out += [" *,"] + + required = sorted((k, v) for k, v in func.params.items() if v.required) + optional = sorted((k, v) for k, v in func.params.items() if not v.required) + + for _, param in required + optional: + out += [gen_function_param(param)] + + # Function return type. + # We wrap the return type annotation for streaming or union responses. + need_quotes = func.streaming or len(func.responses) > 1 + if func.streaming: + func.responses = {code: unwrap_streaming_response(r) for code, r in func.responses.items()} + returntypes = set(annotation(r, prequoted=need_quotes) for r in func.responses.values()) + returntypestr = ",".join(sorted(returntypes)) + if len(returntypes) > 1: + returntypestr = f"typing.Union[{returntypestr}]" + if func.streaming: + returntypestr = f"typing.Iterable[{returntypestr}]" + if need_quotes: + returntypestr = f'"{returntypestr}"' + + out += [f") -> {returntypestr}:"] + + # Function body. + has_path_params = any(p for p in func.params.values() if p.where == "path") + # body_params = sorted(p for p in self.params if func.params[p].where == "body") # not in use + query_params = sorted((k, v) for k, v in func.params.items() if v.where == "query") + + pathstr = f'"{func.path}"' + if has_path_params: + # Happily, we can just generate an f-string based on the path swagger gives us. + pathstr = "f" + pathstr + + if query_params: + out += [" _params = {"] + for _, param in query_params: + if isinstance(param.type, swagger_parser.Bool): + value = f"str({param.name}).lower()" + if not param.required: + value += f" if {param.name} is not None else None" + elif need_parse(param.type): + value = f"{dump(param.type, param.name, omit_unset='True')}" + if not param.required: + value += f" if {param.name} is not None else None" + else: + value = param.name + out += [f' "{param.serialized_name}": {value},'] + out += [" }"] + else: + out += [" _params = None"] + + if "body" in func.params: + # It is important that request bodies omit unset values so that PATCH request bodies + # do not include extraneous None values. + body_param = func.params["body"] + bodystr = dump(body_param.type, body_param.name, "True") + else: + bodystr = "None" + out += [" _resp = session._do_request("] + out += [f' method="{func.method.upper()}",'] + out += [f" path={pathstr},"] + out += [" params=_params,"] + out += [f" json={bodystr},"] + out += [" data=None,"] + out += [" headers=None,"] + out += [" timeout=None,"] + out += [f" stream={func.streaming},"] + out += [" )"] + for expect, returntype in func.responses.items(): + out += [f" if _resp.status_code == {expect}:"] + is_none = isinstance(returntype, swagger_parser.Ref) and ( + returntype.defn is None + or isinstance(returntype.defn, swagger_parser.Class) + and not returntype.defn.params + ) + if not func.streaming: + if is_none: + out += [" return"] + else: + out += [f' return {load(returntype, "_resp.json()")}'] + else: + assert not is_none, "unable to stream empty result class: {func}" + # Too many quotes to do it inline: + yieldable = load(returntype, '_j["result"]') + out += [ + f" for _line in _resp.iter_lines():", + f" _j = json.loads(_line)", + f' if "error" in _j:', + f" raise APIHttpStreamError(", + f' "{func.method}_{func.name}",', + f' runtimeStreamError.from_json(_j["error"])', + f" )", + f" yield {yieldable}", + f" return", + ] + out += [f' raise APIHttpError("{func.method}_{func.name}", _resp)'] + + return "\n".join(out) + + +def gen_class(klass: swagger_parser.Class) -> Code: + required = sorted((k, v) for k, v in klass.params.items() if v.required) + optional = sorted((k, v) for k, v in klass.params.items() if not v.required) + + out = [f"class {klass.name}:"] + for k, v in optional: + out += [f' {k}: "typing.Optional[{annotation(v.type, prequoted=True)}]" = None'] + out += [""] + out += [" def __init__("] + out += [" self,"] + out += [" *,"] + for k, v in required + optional: + out += [" " + gen_init_param(v)] + out += [" ):"] + for k, _ in required: + out += [f" self.{k} = {k}"] + for k, _ in optional: + out += [f" if not isinstance({k}, Unset):"] + out += [f" self.{k} = {k}"] + out += [""] + out += [" @classmethod"] + out += [f' def from_json(cls, obj: Json) -> "{klass.name}":'] + out += [' kwargs: "typing.Dict[str, typing.Any]" = {'] + for k, v in required: + if need_parse(v.type): + parsed = load(v.type, f'obj["{k}"]') + else: + parsed = f'obj["{k}"]' + out += [f' "{k}": {parsed},'] + out += [" }"] + for k, v in optional: + if need_parse(v.type): + parsed = load(v.type, f'obj["{k}"]') + parsed = parsed + f' if obj["{k}"] is not None else None' + else: + parsed = f'obj["{k}"]' + out += [f' if "{k}" in obj:'] + out += [f' kwargs["{k}"] = {parsed}'] + out += [" return cls(**kwargs)"] + out += [""] + out += [" def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]:"] + out += [' out: "typing.Dict[str, typing.Any]" = {'] + for k, v in required: + if need_parse(v.type): + parsed = dump(v.type, f"self.{k}", "omit_unset") + else: + parsed = f"self.{k}" + out.append(f' "{k}": {parsed},') + out += [" }"] + for k, v in optional: + if need_parse(v.type): + parsed = dump(v.type, f"self.{k}", "omit_unset") + parsed = f"None if self.{k} is None else {parsed}" + else: + parsed = f"self.{k}" + out += [f' if not omit_unset or "{k}" in vars(self):'] + out += [f' out["{k}"] = {parsed}'] + out += [" return out"] + + return "\n".join(out) + + +def gen_enum(enum: swagger_parser.Enum) -> Code: + out = [f"class {enum.name}(enum.Enum):"] + out += [f' {v} = "{v}"' for v in enum.members] + return "\n".join(out) + + +def gen_def(anno: swagger_parser.TypeDef) -> Code: + if isinstance(anno, swagger_parser.Class): + return gen_class(anno) + if isinstance(anno, swagger_parser.Enum): + return gen_enum(anno) + assert_never(anno) + + +def gen_paginated(defs: swagger_parser.TypeDefs) -> typing.List[str]: + paginated = [] + for k, defn in defs.items(): + defn = defs[k] + if defn is None or not isinstance(defn, swagger_parser.Class): + continue + # Note that our goal is to mimic duck typing, so we only care if the "pagination" attribute + # exists with a v1Pagination type. + if any(n == "pagination" and p.type.name == "v1Pagination" for n, p in defn.params.items()): + paginated.append(defn.name) + + if not paginated: + return [] + + out = [] + out += ["# Paginated is a union type of objects whose .pagination"] + out += ["# attribute is a v1Pagination-type object."] + out += ["Paginated = typing.Union["] + out += [f" {name}," for name in sorted(paginated)] + out += ["]"] + return out + + +def skip_defn(defn: swagger_parser.TypeDef): + return (isinstance(defn, swagger_parser.Class) and not defn.params) or is_streaming_response( + defn + ) + + +def pybindings(swagger: swagger_parser.ParseResult) -> str: + prefix = """ +# Code generated by generate_bindings.py. DO NOT EDIT. +import enum +import json +import math +import typing + +import requests + +if typing.TYPE_CHECKING: + from determined.common import api + +# flake8: noqa +Json = typing.Any + + +# Unset is a type to distinguish between things not set and things set to None. +class Unset: + pass + + +_unset = Unset() + + +def dump_float(val: typing.Any) -> typing.Any: + if math.isnan(val): + return "Nan" + if math.isinf(val): + return "Infinity" if val > 0 else "-Infinity" + return val + + +class APIHttpError(Exception): + # APIHttpError is used if an HTTP(s) API request fails. + def __init__(self, operation_name: str, response: requests.Response) -> None: + self.response = response + self.operation_name = operation_name + self.message = ( + f"API Error: {operation_name} failed: {response.reason}." + ) + + def __str__(self) -> str: + return self.message + + +class APIHttpStreamError(APIHttpError): + # APIHttpStreamError is used if an streaming API request fails mid-stream. + def __init__(self, operation_name: str, error: "runtimeStreamError") -> None: + self.operation_name = operation_name + self.error = error + self.message = ( + f"Stream Error during {operation_name}: {error.message}" + ) + + def __str__(self) -> str: + return self.message + +""".lstrip() + + out = [prefix] + + for _, defn in sorted(swagger.defs.items()): + if defn is None or skip_defn(defn): + continue + out += [gen_def(defn)] + out += [""] + + for _, op in sorted(swagger.ops.items()): + out += [gen_function(op)] + out += [""] + + # Also generate a list of Paginated response types. + out += gen_paginated(swagger.defs) + + return "\n".join(out).strip() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--output", "-o", action="store", required=True, help="output file") + args = parser.parse_args() + + swagger = swagger_parser.parse(SWAGGER) + bindings = pybindings(swagger) + with open(args.output, "w") as f: + print(bindings, file=f) diff --git a/bindings/generate_bindings_ts.py b/bindings/generate_bindings_ts.py new file mode 100644 index 00000000000..ff065dc0d54 --- /dev/null +++ b/bindings/generate_bindings_ts.py @@ -0,0 +1,591 @@ +from dataclasses import dataclass, field +import os +import re +from shutil import copy, rmtree +import swagger_parser +import typing +from typing_extensions import assert_never, Literal + +DIRNAME = os.path.dirname(__file__) +SWAGGER = "proto/build/swagger/determined/api/v1/api.swagger.json" +SWAGGER = os.path.join(DIRNAME, "..", SWAGGER) +STATIC_FOLDER = os.path.join(DIRNAME, "static_ts_files") + +Code = str +SwaggerType = typing.Union[ + swagger_parser.TypeAnno, swagger_parser.TypeDef, swagger_parser.Parameter +] + + +def head(item: typing.Tuple[str, typing.Any]) -> str: + return item[0].lower() + + +def upper_first(string: str) -> str: + return string[0].upper() + string[1:] + + +# simplified, unsafe-ish camel case function +def camel_case(string: str) -> str: + replace_upper = lambda x: x.group(1).upper() + return string[0].lower() + re.sub(r"[^a-zA-Z0-9]([a-zA-Z0-9])", replace_upper, string[1:]) + + +# small class to make adding lines of code slightly easier to read +@dataclass +class IndentedLines: + lines: typing.List[str] = field(default_factory=list, init=False) + commenting: bool = field(default=False, init=False) + indent_level: int = field(default=0) + tab_char: str = field(default=" ") + + def __post_init__(self): + assert self.indent_level >= 0, "indent level cannot be lower than 0" + + def indent(self): + self.indent_level += 1 + + def dedent(self): + self.indent_level = max(self.indent_level - 1, 0) + + def add_line(self, line: str): + comment_char = " * " if self.commenting else "" + self.lines += [(self.tab_char * self.indent_level) + comment_char + line] + + def add_lines(self, lines: typing.List[str]): + for line in lines: + self.add_line(line) + + def start_comment(self): + self.add_line("/**") + self.commenting = True + + def end_comment(self): + self.commenting = False + self.add_line(" */") + + def __iadd__(self, line_or_lines: typing.Union[str, typing.List[str]]) -> "IndentedLines": + if isinstance(line_or_lines, list): + self.add_lines(line_or_lines) + else: + self.add_line(line_or_lines) + return self + + def __str__(self) -> str: + return "\n".join(self.lines) + + +def annotation(anno: swagger_parser.TypeAnno) -> Code: + if isinstance(anno, swagger_parser.Any): + return "any" + if isinstance(anno, swagger_parser.String): + return "string" + if isinstance(anno, (swagger_parser.Float, swagger_parser.Int)): + return "number" + if isinstance(anno, swagger_parser.Bool): + return "boolean" + if isinstance(anno, swagger_parser.DateTime): + return "Date" + if isinstance(anno, swagger_parser.Ref): + if not anno.defn: + return "void" + return anno.name[0].upper() + anno.name[1:] + if isinstance(anno, swagger_parser.Dict): + if isinstance(anno.values, swagger_parser.Any): + return "any" + return f"{{ [key: string]: {annotation(anno.values)}; }}" + if isinstance(anno, swagger_parser.Sequence): + return f"Array<{annotation(anno.items)}>" + assert_never(anno) + + +def longest_common_prefix(members: typing.List[str]) -> str: + prefix = min(members, key=len) + while not all(member.startswith(prefix) for member in members): + prefix = prefix[:-1] + return prefix + + +def gen_def(anno: swagger_parser.TypeDef) -> Code: + code = IndentedLines() + if isinstance(anno, swagger_parser.Class): + clean_description = (anno.description or "").replace("\n", " ") + proper_name = upper_first(anno.name) + + code.start_comment() + code += f"{clean_description}" + code += "@export" + code += f"@interface {proper_name}" + code.end_comment() + + code += f"export interface {proper_name} {{" + code.indent() + for _, param in anno.params.items(): + clean_title = (param.title or "").replace("\n", " ") + required_txt = ":" if param.required else "?:" + param_annotation = annotation(param.type) + code.start_comment() + code += f"{clean_title}" + code += f"@type {{{param_annotation}}}" + code += f"@memberof {proper_name}" + code.end_comment() + code += f"{param.name}{required_txt} {param_annotation};" + code.dedent() + code += "}" + return str(code) + if isinstance(anno, swagger_parser.Enum): + clean_description = (anno.description or "").replace("\n", " ") + proper_name = anno.name[0].upper() + anno.name[1:] + + code.start_comment() + code += clean_description + code += "@export" + code += "@enum {string}" # parser assumes all enums are strings + code.end_comment() + + code += f"export const {proper_name} = {{" + code.indent() + # can't find where this logic exists in the old codegen -- aargh + prefix = longest_common_prefix(anno.members) if len(anno.members) > 1 else "" + for member in anno.members: + clean_member_name = member[len(prefix) :].replace("_", "") + code += f"{clean_member_name}: '{member}'," + code.dedent() + code += "} as const" + + code += f"export type {proper_name} = ValueOf" + return str(code) + assert_never(anno) + + +Phase = Literal["params", "fp", "factory", "api"] + + +def generate_function(api: str, phase: Phase, function: swagger_parser.Function) -> Code: + indent_level = 1 if phase == "api" else 2 + code = IndentedLines(indent_level) + function_name = camel_case(function.name) + + params_by_location: typing.Dict[str, typing.List[swagger_parser.Parameter]] = {} + for param in function.params.values(): + params_by_location[param.where] = params_by_location.get(param.where, []) + params_by_location[param.where].append(param) + + params_in_order = ( + params_by_location.get("path", []) + + params_by_location.get("query", []) + + params_by_location.get("body", []) + ) + + function_args = ", ".join( + f"{param.name}{'' if param.required else '?'}: {annotation(param.type)}" + for param in params_in_order + ) + call_list = ", ".join(param.name for param in params_in_order) + if function_args: + function_args += ", " + call_list += ", " + + jsdoc: typing.List[str] = [] + if function.summary is not None: + summary = function.summary.replace("\n", " ") + jsdoc += [f"@summary {summary}"] + for param in params_in_order: + param_name = param.name if param.required else f"[{param.name}]" + jsdoc += [ + f"@param {{{annotation(param.type)}}} {param_name} {param.title or ''}".replace( + "\n", " " + ).strip() + ] + jsdoc += ["@param {*} [options] Override http request option."] + jsdoc += ["@throws {RequiredError}"] # does the code generator throw anything else? + + if phase == "params": + code.start_comment() + code += "" # something might be missing here + code += jsdoc + code.end_comment() + + code += f"{function_name}({function_args}options: any = {{}}): FetchArgs {{" + code.indent() + for param in params_in_order: + if param.required: + code += f"// verify required parameter '{param.name}' is not null or undefined" + code += f"if ({param.name} === null || {param.name} === undefined) {{" + code.indent() + code += f"throw new RequiredError('{param.name}','Required parameter {param.name} was null or undefined when calling {function_name}.');" + code.dedent() + code += "}" + path_params = params_by_location.get("path") + fixed_path = re.sub( + r"{(\w*)}", lambda match: f"{{{camel_case(match.group(1))}}}", function.path + ) + code += f"const localVarPath = `{fixed_path}`{'' if path_params else ';'}" + if path_params: + code.indent() + for n, param in enumerate(path_params, start=1): + line = ( + f'.replace(`{{${{"{param.name}"}}}}`, encodeURIComponent(String({param.name})))' + ) + code += f"{line}{';' if n == len(path_params) else ''}" + code.dedent() + code += "const localVarUrlObj = url.parse(localVarPath, true);" + code += ( + f"const localVarRequestOptions = {{ method: '{function.method.upper()}', ...options }};" + ) + code += "const localVarHeaderParameter = {} as any;" + code += "const localVarQueryParameter = {} as any;" + code += "" + + # should only need bearer token auth -- if this breaks, handle parsing auth types + if function.needs_auth: + code += "// authentication BearerToken required" + code += "if (configuration && configuration.apiKey) {" + code.indent() + code += "const localVarApiKeyValue = typeof configuration.apiKey === 'function'" + code.indent() + code += '? configuration.apiKey("Authorization")' + code += ": configuration.apiKey;" + code.dedent() + code += 'localVarHeaderParameter["Authorization"] = localVarApiKeyValue;' + code.dedent() + code += "}" + code += "" + + for param in params_by_location.get("query", []): + null_check = ( + " !== undefined" + if isinstance(param.type, (swagger_parser.Sequence, swagger_parser.DateTime)) + else "" + ) + code += f"if ({param.name}{null_check}) {{" + code.indent() + if isinstance(param.type, swagger_parser.DateTime): + code += f"localVarQueryParameter['{param.serialized_name}'] = {param.name}.toISOString()" + else: + code += f"localVarQueryParameter['{param.serialized_name}'] = {param.name}" + code.dedent() + code += "}" + code += "" + + # should only be one required body parameter that's application/json? if + # this breaks, handle parsing consumption types + body_param = next(iter(params_by_location.get("body", [])), None) + if body_param: + code += "localVarHeaderParameter['Content-Type'] = 'application/json';" + code += "" + + code += "localVarUrlObj.query = { ...localVarUrlObj.query, ...localVarQueryParameter, ...options.query };" + code += "// fix override query string Detail: https://stackoverflow.com/a/7517673/1077943" + code += "localVarUrlObj.search = null;" + code += ( + "localVarRequestOptions.headers = { ...localVarHeaderParameter, ...options.headers };" + ) + + if body_param: + if not isinstance(body_param.type, swagger_parser.String): + code += f"localVarRequestOptions.body = JSON.stringify({body_param.name})" + else: + code += "const needsSerialization = localVarRequestOptions.headers['Content-Type'] === 'application/json';" + code += f"localVarRequestOptions.body = needsSerialization ? JSON.stringify({body_param.name}) : {body_param.name}" + + code += "" + code += "return {" + code.indent() + code += "url: url.format(localVarUrlObj)," + code += "options: localVarRequestOptions," + code.dedent() + code += "};" + code.dedent() + code += "}," + + return str(code) + if phase == "fp": + code.start_comment() + code += "" + code += jsdoc + code.end_comment() + + success_response = function.responses.get("200") + assert success_response, function + + code += f"{function_name}({function_args}options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<{annotation(success_response)}> {{" + code.indent() + code += f"const localVarFetchArgs = {api}FetchParamCreator(configuration).{function_name}({call_list}options);" + code += "return (fetch: FetchAPI = portableFetch, basePath: string = BASE_PATH) => {" + code.indent() + code += "return fetch(basePath + localVarFetchArgs.url, localVarFetchArgs.options).then((response) => {" + code.indent() + code += "if (response.status >= 200 && response.status < 300) {" + code.indent() + code += "return response.json();" + code.dedent() + code += "} else {" + code.indent() + code += "throw response;" + code.dedent() + code += "}" + code.dedent() + code += "});" + code.dedent() + code += "};" + code.dedent() + code += "}," + return str(code) + if phase == "factory": + code.start_comment() + code += "" + code += jsdoc + code.end_comment() + + code += f"{function_name}({function_args}options?: any) {{" + code.indent() + code += ( + f"return {api}Fp(configuration).{function_name}({call_list}options)(fetch, basePath);" + ) + code.dedent() + code += "}," + return str(code) + if phase == "api": + code.start_comment() + code += "" + code += jsdoc + code += f"@memberof {api}" + code.end_comment() + + code += f"public {function_name}({function_args}options?: any) {{" + code.indent() + code += f"return {api}Fp(this.configuration).{function_name}({call_list}options)(this.fetch, this.basePath)" + code.dedent() + code += "}" + code += "" + return str(code) + + assert_never(phase) + + +def generate_api(tag: str, functions: typing.List[swagger_parser.Function]) -> Code: + code = IndentedLines() + api_name = f"{tag}Api" + functions_in_order = sorted(functions, key=lambda f: f.name.lower()) + + # fetch param creator + code.start_comment() + code += f"{api_name} - fetch parameter creator" + code += "@export" + code.end_comment() + code += ( + f"export const {api_name}FetchParamCreator = function (configuration?: Configuration) {{" + ) + code.indent() + code += "return {" + # resetting indent level here so the function generator can take over + cur_indent_level = code.indent_level + code.indent_level = 0 + for function in functions_in_order: + code += generate_function(api_name, "params", function) + code.indent_level = cur_indent_level + code += "}" + code.dedent() + code += "};" + code += "" + + # functional programming interface + code.start_comment() + code += f"{api_name} - functional programming interface" + code += "@export" + code.end_comment() + code += f"export const {api_name}Fp = function (configuration?: Configuration) {{" + code.indent() + code += "return {" + cur_indent_level = code.indent_level + code.indent_level = 0 + for function in functions_in_order: + code += generate_function(api_name, "fp", function) + code.indent_level = cur_indent_level + code += "}" + code.dedent() + code += "};" + code += "" + + # factory interface + code.start_comment() + code += f"{api_name} - factory interface" + code += "@export" + code.end_comment() + code += f"export const {api_name}Factory = function (configuration?: Configuration, fetch?: FetchAPI, basePath?: string) {{" + code.indent() + code += "return {" + cur_indent_level = code.indent_level + code.indent_level = 0 + for function in functions_in_order: + code += generate_function(api_name, "factory", function) + code.indent_level = cur_indent_level + code += "}" + code.dedent() + code += "};" + code += "" + + # class interface + code.start_comment() + code += f"{api_name} - object-oriented interface" + code += "@export" + code += "@class" + code += "@extends {BaseAPI}" + code.end_comment() + code += f"export class {api_name} extends BaseAPI {{" + for function in functions_in_order: + code += generate_function(api_name, "api", function) + code += "}" + code += "" + return str(code) + + +def tsbindings(swagger: swagger_parser.ParseResult) -> str: + clean_description = swagger.info.description.replace("\n", " ") + prelude = f""" +/** + * {swagger.info.title} + * {clean_description} + * + * OpenAPI spec version: {swagger.info.version} + * Contact: {swagger.info.contact} + * + * NOTE: Do not edit the class manually. + */ + + +import url from "url"; +import portableFetch from "portable-fetch"; +import {{ Configuration }} from "./configuration"; + +type ValueOf = T[keyof T]; +const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = {{ + csv: ",", + ssv: " ", + tsv: "\\t", + pipes: "|", +}}; + +/** + * + * @export + * @interface FetchAPI + */ +export interface FetchAPI {{ + (url: string, init?: any): Promise; +}} + +/** + * + * @export + * @interface FetchArgs + */ +export interface FetchArgs {{ + url: string; + options: any; +}} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI {{ + protected configuration: Configuration; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected fetch: FetchAPI = portableFetch) {{ + if (configuration) {{ + this.configuration = configuration; + this.basePath = configuration.basePath || this.basePath; + }} + }} +}}; + +/** + * + * @export + * @class RequiredError + * @extends {{Error}} + */ +export class RequiredError extends Error {{ + name: "RequiredError" + constructor(public field: string, msg?: string) {{ + super(msg); + }} +}} + +""" + out = [prelude] + + # workaround for streaming function behavior + runtime_stream_error = swagger.defs["runtimeStreamError"] + assert runtime_stream_error + runtime_stream_error_ref = swagger_parser.Ref(name="RuntimeStreamError") + swagger_parser.Ref.all_refs.append(runtime_stream_error_ref) + runtime_stream_error_ref.defn = runtime_stream_error + runtime_stream_error_ref.linked = True + + ops_by_tag = {} + for defn in swagger.ops.values(): + # fix naming conventions to match swagger-codegen here + defn.name = camel_case(defn.name) + for param in defn.params.values(): + param.name = camel_case(param.name) + # group ops by tag name + for tag in defn.tags: + fixed_tag = upper_first(camel_case(tag)) + ops_by_tag[fixed_tag] = ops_by_tag.get(fixed_tag, []) + ops_by_tag[fixed_tag].append(defn) + + for _, defn in sorted(swagger.defs.items(), key=head): + if defn is None: + continue + out += [gen_def(defn)] + + for tag, functions in sorted(ops_by_tag.items(), key=head): + out += [generate_api(tag, functions)] + + return "\n".join(out).strip() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--output", "-o", action="store", required=True, help="output folder") + args = parser.parse_args() + + swagger = swagger_parser.parse(SWAGGER) + bindings = tsbindings(swagger) + + if os.path.isdir(args.output): + rmtree(args.output, ignore_errors=True) + os.makedirs(args.output) + + api_path = os.path.join(args.output, "api.ts") + with open(api_path, "w") as f: + print(bindings, file=f) + + clean_description = swagger.info.description.replace("\n", " ") + for path in os.listdir(STATIC_FOLDER): + path = os.path.join(STATIC_FOLDER, path) + if os.path.isfile(path): + if path.endswith(".template"): + with open(path, "r") as f: + contents = f.read() + contents = contents.format(swagger=swagger, clean_description=clean_description) + destination = os.path.join( + args.output, os.path.basename(path).replace(".template", "") + ) + print(destination) + with open(destination, "w") as f: + print(contents, file=f) + else: + copy(path, args.output) diff --git a/bindings/requirements.txt b/bindings/requirements.txt new file mode 100644 index 00000000000..3492fa57976 --- /dev/null +++ b/bindings/requirements.txt @@ -0,0 +1 @@ +typing-extensions diff --git a/bindings/static_ts_files/configuration.ts.template b/bindings/static_ts_files/configuration.ts.template new file mode 100644 index 00000000000..a32be2f7e91 --- /dev/null +++ b/bindings/static_ts_files/configuration.ts.template @@ -0,0 +1,63 @@ +/** + * {swagger.info.title} + * {clean_description} + * + * OpenAPI spec version: {swagger.info.version} + * Contact: {swagger.info.contact} + * + * NOTE: Do not edit the class manually. + */ + + +export interface ConfigurationParameters {{ + apiKey?: string | ((name: string) => string); + username?: string; + password?: string; + accessToken?: string | ((name: string, scopes?: string[]) => string); + basePath?: string; +}} + +export class Configuration {{ + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | ((name: string) => string); + /** + * parameter for basic security + * + * @type {{string}} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {{string}} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | ((name: string, scopes?: string[]) => string); + /** + * override base path + * + * @type {{string}} + * @memberof Configuration + */ + basePath?: string; + + constructor(param: ConfigurationParameters = {{}}) {{ + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + }} +}} diff --git a/bindings/static_ts_files/custom.d.ts b/bindings/static_ts_files/custom.d.ts new file mode 100644 index 00000000000..4c611cc3216 --- /dev/null +++ b/bindings/static_ts_files/custom.d.ts @@ -0,0 +1,2 @@ +declare module 'portable-fetch'; +declare module 'url'; diff --git a/bindings/static_ts_files/index.ts.template b/bindings/static_ts_files/index.ts.template new file mode 100644 index 00000000000..116fe82429d --- /dev/null +++ b/bindings/static_ts_files/index.ts.template @@ -0,0 +1,13 @@ +/** + * {swagger.info.title} + * {clean_description} + * + * OpenAPI spec version: {swagger.info.version} + * Contact: {swagger.info.contact} + * + * NOTE: Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; diff --git a/bindings/swagger_parser.py b/bindings/swagger_parser.py new file mode 100644 index 00000000000..0b504228844 --- /dev/null +++ b/bindings/swagger_parser.py @@ -0,0 +1,407 @@ +from dataclasses import dataclass, field +import json +import sys +import typing +import typing_extensions + + +class Any: + def __repr__(self): + return "Any" + + +class String: + def __repr__(self): + return "str" + + +class DateTime: + def __repr__(self): + return "DateTime" + + +class Float: + def __repr__(self): + return "float" + + +class Int: + def __repr__(self): + return "int" + + +class Bool: + def __repr__(self): + return "bool" + + +@dataclass +class Dict: + values: "TypeAnno" + + def __repr__(self): + return f"Dict[str, {self.values}]" + + +@dataclass +class Sequence: + items: "TypeAnno" + + def __repr__(self): + return f"Sequence[{self.items}]" + + +@dataclass +class Ref: + # Collect refs as we instantiate them, for the linking step. + all_refs: typing.ClassVar[typing.List["Ref"]] = [] + + name: str + url_encodable: bool = False + linked: bool = field(default=False, init=False) + defn: typing.Optional["TypeDef"] = field(default=None, init=False) + + def __post_init__(self): + Ref.all_refs.append(self) + + def __repr__(self): + return self.name + + +TypeAnno = typing.Union[Sequence, Dict, Float, Ref, Any, String, Int, Bool, DateTime] + + +@dataclass +class Parameter: + name: str + type: TypeAnno + required: bool + where: typing_extensions.Literal["query", "body", "path", "definitions"] + serialized_name: typing.Optional[str] = None + title: typing.Optional[str] = None + + def __post_init__(self): + # validations + assert self.where in ("query", "body", "path", "definitions"), (self.name, self.where) + assert self.where != "path" or self.required, self.name + if self.where == "path": + if not isinstance(self.type, (String, Int, Bool)): + raise AssertionError(f"bad type in path parameter {self.name}: {self.type}") + if self.where == "query": + underlying_typ = self.type.items if isinstance(self.type, Sequence) else self.type + if not isinstance(underlying_typ, (String, Int, Bool, DateTime)): + if not (isinstance(underlying_typ, Ref) and underlying_typ.url_encodable): + raise AssertionError(f"bad type in query parameter {self.name}: {self.type}") + + +@dataclass +class Class: + name: str + params: typing.Dict[str, Parameter] + description: typing.Optional[str] + + +@dataclass +class Enum: + name: str + members: typing.List[str] + description: typing.Optional[str] + + +TypeDef = typing.Union[Class, Enum] + +TypeDefs = typing.Dict[str, typing.Optional[TypeDef]] + + +@dataclass +class Function: + name: str + method: str + path: str + params: typing.Dict[str, Parameter] + responses: typing.Dict[str, TypeAnno] + streaming: bool + tags: typing.Set[str] + summary: str + needs_auth: bool + + def __repr__(self) -> str: + out = ( + f"Function({self.name}):\n" + f" self.method = {self.method.upper()}\n" + f" self.params = {self.params}\n" + f" responses = {{" + ) + for code, resp in self.responses.items(): + out += f"\n {code} = {resp}" + out += "\n }" + return out + + +@dataclass +class ApiInfo: + title: str + description: str + version: str + contact: str + + +@dataclass +class ParseResult: + defs: TypeDefs + ops: typing.Dict[str, Function] + info: ApiInfo + + +def classify_type(enums: dict, path: str, schema: dict) -> TypeAnno: + # enforce valid jsonschema: + assert isinstance(schema, dict), (path, schema) + if "enum" in schema: + name = enums[json.dumps(schema["enum"])] + assert name, (name, schema) + return Ref(name, url_encodable=True) + + if "$ref" in schema: + ref = schema["$ref"] + start = "#/definitions/" + assert ref.startswith(start), ref + return Ref(ref[len(start) :]) + + if "type" not in schema: + # When "type" is not present, any json element should be valid. + return Any() + + # only $refs don't have types + assert "type" in schema, (path, schema) + + if schema["type"] == "string": + if schema.get("format") == "date-time": + return DateTime() + return String() + + if schema["type"] == "integer": + return Int() + + if schema["type"] == "boolean": + return Bool() + + if schema["type"] in ("float", "number"): + return Float() + + if schema["type"] == "object" and "properties" not in schema: + adlProps = schema.get("additionalProperties") + if adlProps is None: + return Dict(Any()) + return Dict(classify_type(enums, path + ".additionalProperties", adlProps)) + + if schema["type"] == "array": + items = schema.get("items") + if items is None: + raise ValueError(path, schema) + return Sequence(classify_type(enums, path + ".items", items)) + + raise ValueError(f"unhandled schema: {schema} @ {path}") + + +def process_enums(swagger_definitions: dict) -> typing.Dict[int, str]: + """ + Process enums from swagger definitions. In OpenAPI spec v2 generated + by protoc-gen-openapi enums are not linked to a definition and are inlined. + Here we preprocess them so that they can be linked to a definition. + """ + enums = {} + for name, schema in swagger_definitions.items(): + if "enum" in schema: + members = schema["enum"] + if enums.get(json.dumps(members)) is not None: + print( + "ambiguous enum parameter:", + name, + members, + file=sys.stderr, + ) + enums[json.dumps(members)] = name + return enums + + +def classify_definition(enums: dict, name: str, schema: dict): + path = name + if "enum" in schema: + if schema["type"] == "string": + members = schema["enum"] + return Enum(name, members, schema["description"]) + raise ValueError("unhandled enum type ({schema['type']}): {schema}") + + if schema["type"] == "object": + # top-level named objects should be classes, not typed dictionaries: + assert "additionalProperties" not in schema, (name, schema) + required = set(schema.get("required", [])) + members = { + k: Parameter( + name=k, + type=classify_type(enums, f"{path}.{k}", v), + required=(k in required), + where="definitions", + serialized_name=None, + title=v.get("title") or v.get("description"), + ) + for k, v in schema.get("properties", {}).items() + } + description = schema.get("description") + return Class(name, members, description) + raise ValueError(f"unhandled schema: {schema} @ {path}") + + +def process_definitions(swagger_definitions: dict, enums: dict) -> TypeDefs: + return { + name: classify_definition(enums, name, schema) + for name, schema in swagger_definitions.items() + } + + +def is_expected_path(text: str) -> bool: + """ + Check if any dots appear outside of curly braces, if any. + This is assuming there are no nested curly braces. + """ + in_braces = False + for c in text: + if c == "{": + in_braces = True + elif c == "}": + in_braces = False + elif c == "." and not in_braces: + return False + return True + + +def process_paths( + swagger_paths: dict, enums: dict +) -> typing.Tuple[typing.Dict[str, Function], typing.Dict[str, Class]]: + ops = {} + extra_classes = {} + for path, methods in swagger_paths.items(): + for method, spec in methods.items(): + name = spec["operationId"] + # Figure out response types. + responses = {} + streaming = None + bad_op = False + for code, rspec in spec["responses"].items(): + rschema = rspec["schema"] + if code == "default": + # We expect all "default" responses to be runtimeErrors, and we ignore them. + default_type = classify_type(enums, f"{name}.responses.default", rschema) + assert isinstance(default_type, Ref), rschema + assert default_type.name == "runtimeError", rschema + # Safe to ignore this return type. + continue + + if rschema.get("type") == "": + # not a valid response schema, skipping + bad_op = True + break + + if rschema.get("title", "").startswith("Stream result"): + # We expect a specific structure to streaming endpoints. + assert rschema["type"] == "object", rschema + assert "additionalProperties" not in rschema, rschema + rprops = rschema["properties"] + assert set(rprops.keys()) == set(("result", "error")), rschema + error_type = classify_type( + enums, f"{name}.responses.{code}.properties.error", rprops["error"] + ) + # We expect all "error" results to be runtimeStreamError. They are parsed in + # code generated by Function.gen_def(). + assert isinstance(error_type, Ref), rschema + assert error_type.name == "runtimeStreamError", rschema + if streaming is False: + raise ValueError( + f"a method must be either all-streaming or all-nonstreaming: {rspec}" + ) + streaming = True + + # handle inlined objects similar to how swagger-codegen does + result_type_class = classify_definition( + enums, f"{name}.response.{code}", rschema + ) + assert isinstance(result_type_class, Class) + result_type_class_ref = result_type_class.params["result"].type + assert isinstance(result_type_class_ref, Ref) + pascal_name = result_type_class_ref.name + pascal_name = pascal_name[0].upper() + pascal_name[1:] + result_type_class.name = f"StreamResultOf{pascal_name}" + result_type = Ref(result_type_class.name) + extra_classes[result_type_class.name] = result_type_class + + responses[code] = result_type + continue + + responses[code] = classify_type(enums, f"{name}.responses.{code}", rschema) + if streaming is True: + raise ValueError( + f"a method must be either all-streaming or all-nonstreaming: {rspec}" + ) + streaming = False + + if bad_op: + continue + + assert streaming is not None + + # Figure out parameters. + params = {} + for pspec in spec.get("parameters", []): + where = pspec["in"] + serialized_name = None + if where == "query": # preserve query parameter names + serialized_name = pname = pspec["name"] + pname = pspec["name"].replace(".", "_") + required = pspec.get("required", False) + if "schema" in pspec: + pschema = pspec["schema"] + else: + # swagger has some weird inlining going on here... + inlined = ("type", "format", "items", "properties", "enum") + pschema = {k: pspec[k] for k in inlined if k in pspec} + ptype = classify_type(enums, f"{name}.{pname}", pschema) + title = pspec.get("title") or pspec.get("description") + params[pname] = Parameter(pname, ptype, required, where, serialized_name, title) + + assert is_expected_path(path), (path, name) + path = path.replace(".", "_") + tags = set(spec["tags"]) + summary = spec["summary"] + needs_auth = "security" not in spec + op = Function( + name, method, path, params, responses, streaming, tags, summary, needs_auth + ) + ops[name] = op + return ops, extra_classes + + +def link_all_refs(defs: TypeDefs) -> None: + for ref in Ref.all_refs: + ref.linked = True + ref.defn = defs[ref.name] + + +def parse(path: str) -> ParseResult: + with open(path) as f: + swagger_json = json.load(f) + enums = process_enums(swagger_json["definitions"]) + defs = process_definitions(swagger_json["definitions"], enums) + ops, streaming_refs = process_paths(swagger_json["paths"], enums) + defs.update(streaming_refs) + link_all_refs(defs) + + info_keys = ("title", "description", "version") + info_json = swagger_json["info"] + info = ApiInfo( + **{ + **{k: info_json[k] for k in info_keys if k in info_json}, + **{"contact": info_json["contact"]["email"]}, + } + ) + + return ParseResult(ops=ops, defs=defs, info=info) diff --git a/cli/determined_cli/__version__.py b/cli/determined_cli/__version__.py index 35775c59a65..d4f7707f462 100644 --- a/cli/determined_cli/__version__.py +++ b/cli/determined_cli/__version__.py @@ -1 +1 @@ -__version__ = "0.19.12-dev0" +__version__ = "0.20.1-dev0" diff --git a/cli/setup.py b/cli/setup.py index eb3d1c25df2..5c8ec31d18b 100644 --- a/cli/setup.py +++ b/cli/setup.py @@ -2,7 +2,7 @@ setup( name="determined-cli", - version="0.19.12-dev0", + version="0.20.1-dev0", author="Determined AI", author_email="hello@determined.ai", url="https://determined.ai/", @@ -13,6 +13,6 @@ packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), python_requires=">=3.6", install_requires=[ - "determined==0.19.12-dev0", + "determined==0.20.1-dev0", ], ) diff --git a/common/determined_common/__version__.py b/common/determined_common/__version__.py index 35775c59a65..d4f7707f462 100644 --- a/common/determined_common/__version__.py +++ b/common/determined_common/__version__.py @@ -1 +1 @@ -__version__ = "0.19.12-dev0" +__version__ = "0.20.1-dev0" diff --git a/common/setup.py b/common/setup.py index 1a436eee370..b41ae7c2ff1 100644 --- a/common/setup.py +++ b/common/setup.py @@ -2,7 +2,7 @@ setup( name="determined-common", - version="0.19.12-dev0", + version="0.20.1-dev0", author="Determined AI", author_email="hello@determined.ai", url="https://determined.ai/", @@ -14,7 +14,7 @@ python_requires=">=3.6", package_data={"determined.common": ["py.typed"]}, install_requires=[ - "determined==0.19.12-dev0", + "determined==0.20.1-dev0", ], zip_safe=False, ) diff --git a/deploy/determined_deploy/__version__.py b/deploy/determined_deploy/__version__.py index 35775c59a65..d4f7707f462 100644 --- a/deploy/determined_deploy/__version__.py +++ b/deploy/determined_deploy/__version__.py @@ -1 +1 @@ -__version__ = "0.19.12-dev0" +__version__ = "0.20.1-dev0" diff --git a/deploy/setup.py b/deploy/setup.py index 6055d5ba9ec..6605f182074 100644 --- a/deploy/setup.py +++ b/deploy/setup.py @@ -2,7 +2,7 @@ setup( name="determined-deploy", - version="0.19.12-dev0", + version="0.20.1-dev0", author="Determined AI", author_email="hello@determined.ai", url="https://determined.ai/", @@ -13,6 +13,6 @@ packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), python_requires=">=3.6", install_requires=[ - "determined==0.19.12-dev0", + "determined==0.20.1-dev0", ], ) diff --git a/docs/architecture/index.rst b/docs/architecture/index.rst index a85c0079d18..3c8dec297ac 100644 --- a/docs/architecture/index.rst +++ b/docs/architecture/index.rst @@ -1,25 +1,33 @@ -##################### - System Architecture -##################### +###################### + How Determined Works +###################### -Determined consists of a single **master** and one or more **agents**. There is typically one agent -per compute server; a single machine can serve as both a master and an agent. +With Determined you can: -The **master** is the central component of the Determined system. It is responsible for +- Use state-of-the-art distributed training to train models faster without changing model code. +- Automatically find high-quality models using advanced hyperparameter tuning. +- Get more from your GPUs and reduce cloud GPU costs with preemptible instances and smart + scheduling. +- Leverage experiment tracking out-of-the-box to track and reproduce your work, tracking code + versions, metrics, checkpoints, and hyperparameters. +- Continue using popular deep learning libraries, such as TensorFlow, Keras, and PyTorch by simply + integrating the Determined API with your existing model code. -- Storing experiment, trial, and workload metadata. -- Scheduling and dispatching work to agents. -- Managing provisioning and deprovisioning of agents in clouds. -- Advancing the experiment, trial, and workload state machines over time. -- Hosting the WebUI and the REST API. +Determined integrates these features into an easy-to-use, high-performance deep learning environment +so you can spend your time building models instead of managing infrastructure. -An **agent** manages a number of **slots**, which are computing devices (typically a GPU or CPU). An -agent has no state and only communicates with the master. Each agent is responsible for +Learn more: -- Discovering local computing devices (slots) and sending metadata about them to the master. -- Running the workloads that are requested by the master. -- Monitoring containers and sending information about them to the master. +- :doc:`Intro to Determined `: Conceptual information about Determined including its + features and benefits. +- :doc:`System Architecture `: Learn about the main components of the + Determined system architecture. +- :doc:`Distributed Training <../training/dtrain-introduction>`: A conceptual overview of + distributed training with Determined. -The **trial runner** runs a trial in a containerized environment. So the trial runners are expected -to have access to the data that will be used in training. The **agents** are responsible for -reporting the states of **trial runner** to the master. +.. toctree:: + :caption: How Determined Works + :hidden: + + introduction + system-architecture diff --git a/docs/introduction.rst b/docs/architecture/introduction.rst similarity index 99% rename from docs/introduction.rst rename to docs/architecture/introduction.rst index 8c0f9b813e0..a7b76bb4cbf 100644 --- a/docs/introduction.rst +++ b/docs/architecture/introduction.rst @@ -1,5 +1,3 @@ -:orphan: - ############################ Introduction to Determined ############################ diff --git a/docs/architecture/system-architecture.rst b/docs/architecture/system-architecture.rst new file mode 100644 index 00000000000..e72d2ec94e1 --- /dev/null +++ b/docs/architecture/system-architecture.rst @@ -0,0 +1,31 @@ +##################### + System Architecture +##################### + +Determined consists of a single **master** and one or more **agents**. There is typically one agent +per compute server; a single machine can serve as both a master and an agent. + +The **master** is the central component of the Determined system. It is responsible for + +- Storing experiment, trial, and workload metadata. +- Scheduling and dispatching work to agents. +- Managing provisioning and deprovisioning of agents in clouds. +- Advancing the experiment, trial, and workload state machines over time. +- Hosting the WebUI and the REST API. + +An **agent** manages a number of **slots**, which are computing devices (typically a GPU or CPU). An +agent has no state and only communicates with the master. Each agent is responsible for + +- Discovering local computing devices (slots) and sending metadata about them to the master. +- Running the workloads that are requested by the master. +- Monitoring containers and sending information about them to the master. + +.. image:: /assets/images/det-ai-sys-arch-01-dark.png + :class: only-dark + +.. image:: /assets/images/det-ai-sys-arch-01-light.png + :class: only-light + +The **task container** runs a training task or other task(s) in a containerized environment. +Training tasks are expected to have access to the data that will be used in training. The **agents** +are responsible for reporting the status of the **task container** to the master. diff --git a/docs/assets/images/det-ai-sys-arch-01-dark.png b/docs/assets/images/det-ai-sys-arch-01-dark.png new file mode 100644 index 00000000000..672ec782eb0 Binary files /dev/null and b/docs/assets/images/det-ai-sys-arch-01-dark.png differ diff --git a/docs/assets/images/det-ai-sys-arch-01-light.png b/docs/assets/images/det-ai-sys-arch-01-light.png new file mode 100644 index 00000000000..cfcd77a6005 Binary files /dev/null and b/docs/assets/images/det-ai-sys-arch-01-light.png differ diff --git a/docs/cluster-setup-guide/deploy-cluster/index.rst b/docs/cluster-setup-guide/deploy-cluster/index.rst index c1ea953bc80..75b9beaa1f1 100644 --- a/docs/cluster-setup-guide/deploy-cluster/index.rst +++ b/docs/cluster-setup-guide/deploy-cluster/index.rst @@ -1,24 +1,41 @@ -#################### - Cluster Deployment -#################### +################### + Set Up Determined +################### -The Cluster Deployment user guides provide instructions on deploying Determined on premise, on -cloud, and in kubernetes environments: +To set up Determined, start by following the cluster deployment guide for your environment. -+------------------------------------------+-----------------------------------------------------+ -| Title | Description | -+==========================================+=====================================================+ -| :doc:`sysadmin-deploy-on-prem/overview` | How to deploy Determined on-premises. | -+------------------------------------------+-----------------------------------------------------+ -| :doc:`sysadmin-deploy-on-aws/overview` | How to deploy Determined on Amazon Web Services. | -+------------------------------------------+-----------------------------------------------------+ -| :doc:`sysadmin-deploy-on-gcp/overview` | How to deploy Determined on Google Cloud Platform. | -+------------------------------------------+-----------------------------------------------------+ -| :doc:`sysadmin-deploy-on-k8s/overview` | How to run Determined on Kubernetes. | -+------------------------------------------+-----------------------------------------------------+ -| :doc:`sysadmin-deploy-on-slurm/overview` | How to run Determined on an HPC cluster | -| | (Slurm/PBS). | -+------------------------------------------+-----------------------------------------------------+ ++--------------------------------------------------------+ +| Environment | ++========================================================+ +| :doc:`sysadmin-deploy-on-prem/overview` | +| | +| - :doc:`sysadmin-deploy-on-prem/linux-packages` | +| - :doc:`sysadmin-deploy-on-prem/deploy` | +| - :doc:`sysadmin-deploy-on-prem/docker` | +| - :doc:`sysadmin-deploy-on-prem/homebrew` | ++--------------------------------------------------------+ +| :doc:`sysadmin-deploy-on-aws/overview` | ++--------------------------------------------------------+ +| :doc:`sysadmin-deploy-on-gcp/overview` | ++--------------------------------------------------------+ +| :doc:`sysadmin-deploy-on-k8s/overview` | +| | +| - :doc:`sysadmin-deploy-on-k8s/install-on-kubernetes` | +| - :doc:`sysadmin-deploy-on-k8s/setup-aks-cluster` | +| - :doc:`sysadmin-deploy-on-k8s/setup-eks-cluster` | +| - :doc:`sysadmin-deploy-on-k8s/setup-gke-cluster` | ++--------------------------------------------------------+ +| :doc:`sysadmin-deploy-on-slurm/overview` | ++--------------------------------------------------------+ + +************************************ + Configuring the Determined Cluster +************************************ + +- :doc:`Common configuration options ` +- :doc:`Master configuration reference + ` +- :doc:`Agent configuration reference ` .. toctree:: :hidden: diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-gcp/install-gcp.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-gcp/install-gcp.rst index ff39daac6af..4de0171578b 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-gcp/install-gcp.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-gcp/install-gcp.rst @@ -406,5 +406,5 @@ This command line will spin up a cluster of up to 2 A100s in the ``us-central1-c --compute-agent-instance-type a2-highgpu-1g --gpu-num 1 \ --gpu-type nvidia-tesla-a100 \ --region us-central1 --zone us-central1-c \ - --gpu-env-image determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 \ - --cpu-env-image determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12 + --gpu-env-image determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 \ + --cpu-env-image determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1 diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/hpc-launching-architecture.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/hpc-launching-architecture.rst index e078edf1c8c..5b74f3e0510 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/hpc-launching-architecture.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/hpc-launching-architecture.rst @@ -190,6 +190,9 @@ provided that they do not conflict with Determined-controlled settings. +------------------------+-------------------------------------------------------------------------------------------+ | ``--wckey`` | A value identified by the ``resource_manager.job_project_source`` configuration. | +------------------------+-------------------------------------------------------------------------------------------+ +| ``--no-requeue`` | Disable any potential automatic requeue of the job by SLURM. Determined will handle the | +| | checkpoint and restart for its experiments. | ++------------------------+-------------------------------------------------------------------------------------------+ Slurm Resource Calculations --------------------------- diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/install-on-slurm.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/install-on-slurm.rst index b8c1e4aeb5a..33b878686b7 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/install-on-slurm.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/install-on-slurm.rst @@ -109,7 +109,9 @@ fulfilled and configured, install and configure the Determined master: +----------------------------+----------------------------------------------------------------+ | ``user_name`` and | By default, the launcher runs from the root account. Create a | | ``group_name`` | local account and group and update these values to enable | - | | running from another account. | + | | running from another account. This account must have access to | + | | the Slurm/PBS command line to discover partitions and | + | | summarize cluster usage. | +----------------------------+----------------------------------------------------------------+ | ``path`` | If any of the launcher dependencies are not on the default | | | path, you can override the default by updating this value. | @@ -131,7 +133,7 @@ fulfilled and configured, install and configure the Determined master: #. Verify successful launcher startup using the ``systemctl status launcher`` command. If the launcher fails to start, check system log diagnostics, such as ``/var/log/messages`` or - ``journalctl --since=10m -u launcher``, make the needed changes to the + ``journalctl --since="10 minutes ago" -u launcher``, make the needed changes to the ``/etc/determined/master.yaml`` file, and restart the launcher. If the installer reported incorrect dependencies, verify that they have been resolved by changes @@ -149,8 +151,8 @@ fulfilled and configured, install and configure the Determined master: #. Verify successful determined-master startup using the ``systemctl status determined-master`` command. If the launcher fails to start, check system log diagnostics, such as - ``/var/log/messages`` or ``journalctl --since=10m -u determined-master``, make the needed changes - to the ``/etc/determined/master.yaml`` file, and restart the determined-master. + ``/var/log/messages`` or ``journalctl --since="10 minutes ago" -u determined-master``, make the + needed changes to the ``/etc/determined/master.yaml`` file, and restart the determined-master. #. If the compute nodes of your cluster do not have internet connectivity to download Docker images, see :ref:`slurm-image-config`. diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/overview.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/overview.rst index 8b1cada1d57..da26885de0b 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/overview.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/overview.rst @@ -1,3 +1,5 @@ +.. _sysadmin-deploy-on-hpc: + ##################### Deploy on Slurm/PBS ##################### diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/singularity.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/singularity.rst index 9613de805ad..28470e7626e 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/singularity.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/singularity.rst @@ -24,11 +24,11 @@ by default in this version of Determined are described below. +-------------+-------------------------------------------------------------------------+ | Environment | File Name | +=============+=========================================================================+ -| CPUs | ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c`` | +| CPUs | ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364`` | +-------------+-------------------------------------------------------------------------+ -| Nvidia GPUs | ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c`` | +| Nvidia GPUs | ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364`` | +-------------+-------------------------------------------------------------------------+ -| AMD GPUs | ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c`` | +| AMD GPUs | ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364`` | +-------------+-------------------------------------------------------------------------+ See :doc:`/training/setup-guide/set-environment-images` for the images Docker Hub location, and add diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-known-issues.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-known-issues.rst index e5879552386..ee75c8278b4 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-known-issues.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-known-issues.rst @@ -4,6 +4,19 @@ Known Issues ############## +*********************************************** + Agent-specific Scheduling Options are Ignored +*********************************************** + +When using the HPC Launcher, Determined delegates all job scheduling and prioritization to the HPC +workload manager (either Slurm or PBS) and the following experiment configuration options are +ignored. + +- ``resources.agent_label`` +- ``resources.max_slots`` +- ``resources.priority`` +- ``resources.weight`` + .. _slurm-and-docker-differences: ************************************ @@ -63,24 +76,17 @@ Some constraints are due to differences in behavior between Docker and Singulari :ref:`slurm-image-config` or configure ``SINGULARITY_CACHEDIR`` to point to a shared directory. -- Some Docker features do not have an exact replacement in Singularity. +- Some Docker features do not have an exact replacement in Singularity, and therefore the + associated Determined features are not supported. +--------------------------------------+------------------------------------------------------+ | Feature | Description | +======================================+======================================================+ - | ``resources.agent_label`` | Scheduling is managed by the Slurm workload manager. | - +--------------------------------------+------------------------------------------------------+ | ``resources.devices`` | By default ``/dev`` is mounted from the compute | | | host, so all devices are available. This can be | | | overridden by the ``singularity.conf`` ``mount dev`` | | | option. | +--------------------------------------+------------------------------------------------------+ - | ``resources.max_slots`` | Scheduling is managed by the Slurm workload manager. | - +--------------------------------------+------------------------------------------------------+ - | ``resources.priority`` | Scheduling is managed by the Slurm workload manager. | - +--------------------------------------+------------------------------------------------------+ - | ``resources.weight`` | Scheduling is managed by the Slurm workload manager. | - +--------------------------------------+------------------------------------------------------+ | ``resources.shm_size`` | By default ``/dev/shm`` is mounted from the compute | | | host. This can be overridden by the | | | ``singularity.conf`` ``mount tmp`` option. When | @@ -96,9 +102,18 @@ Some constraints are due to differences in behavior between Docker and Singulari Singularity Known Issues ************************** -Launching a PBS jobs with an experiment configuration which includes an embedded double quote -character (") may cause the job to fail with the json.decoder.JSONDecodeError unless you have -Singularity 3.10 or greater or Apptainer 1.1 or greater. +Launching a PBS job with an experiment configuration that includes an embedded double quote +character (") may cause the job to fail unless you have Singularity 3.10 or greater or Apptainer 1.1 +or greater. For example, the error might be the json.decoder.JSONDecodeError or the experiment log +may contain ``source: /.inject-singularity-env.sh:224:1563: "export" must be followed by names or +assignments`` and ``RuntimeError: missing environment keys [DET_MASTER, DET_CLUSTER_ID, +DET_AGENT_ID, DET_SLOT_IDS, DET_TASK_ID, DET_ALLOCATION_ID, DET_SESSION_TOKEN, DET_TASK_TYPE], is +this running on-cluster?`` + +The version of Singularity is detected by the HPC Launcher invoking the singularity command and +checking for the ``--no-eval`` option. If the singularity command is not on the path for the HPC +launcher or is of an inconsistent version with the compute nodes, embedded double quote characters +may still not work. ************************ Apptainer Known Issues @@ -209,12 +224,18 @@ sometimes resolved by additionally installing the ``apptainer-setuid`` package. slurm_job_name_suffix=$(echo ${SLURM_JOB_NAME} | sed 's/^\S\+-\([a-z0-9]\+-[a-z0-9]\+\)$/\1/') - podman_container_stop_command="podman container stop \ - --filter name='.+-${slurm_job_name_suffix}'" + if ps -fe | grep -E "[p]odman run .*-name ${SLURM_JOB_USER}-\S+-${slurm_job_name_suffix}" > /dev/null + then + timeout -k 15s 15s bash -c "while ps -fe | grep -E \"[c]onmon .*-n ${SLURM_JOB_USER}-\S+-${slurm_job_name_suffix}\" > /dev/null 2>&1; do sleep 1; done" + + podman_container_stop_command="podman container stop --filter name='.+-${slurm_job_name_suffix}'" + + echo "$(date):$0: Running \"${podman_container_stop_command}\"" 1>&2 - echo "$(date):$0: Running \"${podman_container_stop_command}\"" 1>&2 + eval ${podman_container_stop_command} + fi - eval ${podman_container_stop_command} + exit 0 Restart the ``slurmd`` daemon on all compute nodes. @@ -238,6 +259,21 @@ sometimes resolved by additionally installing the ``apptainer-setuid`` package. - Enroot does not provide a mechanism for sharing containers. Each user must create any containers needed by their Determined experiments prior to creating the experiment. +- Some Docker features do not have an exact replacement in Enroot, and therefore the associated + Determined features are not supported. + + +--------------------------------------+------------------------------------------------------+ + | Feature | Description | + +======================================+======================================================+ + | ``resources.devices`` | Managed via Enroot configuration files. | + +--------------------------------------+------------------------------------------------------+ + | ``resources.shm_size`` | Managed via Enroot configuration files. | + +--------------------------------------+------------------------------------------------------+ + | ``environment.registry_auth.server`` | No equivalent setting in Enroot. | + +--------------------------------------+------------------------------------------------------+ + | ``environment.registry_auth.email`` | No equivalent setting in Enroot. | + +--------------------------------------+------------------------------------------------------+ + .. _slurm-known-issues: ******************** diff --git a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-requirements.rst b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-requirements.rst index fb81bf7d2d6..5c0f294dcbc 100644 --- a/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-requirements.rst +++ b/docs/cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-slurm/slurm-requirements.rst @@ -128,6 +128,13 @@ recommended to optimize how Determined interacts with Slurm: default using the :ref:`slurm section ` ``default_compute_resource_pool`` or ``default_aux_resource_pool`` option. + - If a Slurm partition is not homogeneous, you may create a resource pool that provides + homogenous resources out of that partition using a custom resource pool. Configure a + :ref:`resource pool ` with ``provider_type: hpc``, specify the + underlying Slurm partition name to receive the job and include a :ref:`task_container_defaults + ` section with the necessary ``slurm`` options to select the + desired homogenous set of resources from that partition. + - Ensure the ``MaxNodes`` value for each partition is not less than the number of GPUs in the partition. @@ -188,6 +195,13 @@ to optimize how Determined interacts with PBS: using the :ref:`pbs section ` ``default_compute_resource_pool`` or ``default_aux_resource_pool`` option. + - If a PBS queue is not homogeneous, you may create a resource pool that provides homogenous + resources out of that queue using a custom resource pool. Configure a :ref:`resource pool + ` with ``provider_type: hpc``, specify the underlying PBS queue name + to receive the job and include a :ref:`task_container_defaults + ` section with the necessary `pbs`` options to select the + desired homogenous set of resources from that queue. + - Tune the PBS configuration for Determined job preemption. PBS supports a wide variety of criteria to trigger job preemption, and you may use any per your @@ -335,7 +349,7 @@ platform. There may be additional per-user configuration that is required. .. code:: bash - image=determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c + image=determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 cd /shared/enroot/images enroot import docker://$image enroot create /shared/enroot/images/${image//[\/:]/\+}.sqsh diff --git a/docs/conf.py b/docs/conf.py index 71e9e26174e..ec6fad67c89 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,6 +44,7 @@ def setup(app): "examples", "requirements.txt", "site", + "release-notes/README.md", ] html_baseurl = "https://docs.determined.ai" # Base URL for sitemap. highlight_language = "none" @@ -98,6 +99,11 @@ def setup(app): "sphinx_copybutton", "sphinx_sitemap", "sphinx_reredirects", + "myst_parser", +] + +myst_extensions = [ + "colon_fence", ] # Our custom sphinx extension uses this value to decide where to look for @@ -128,34 +134,34 @@ def setup(app): redirects = { "api-pytorch": "training/apis-howto/api-pytorch-ug.html", - "apis-howto/api-core/checkpoints": "../../api-core-ug.html", - "apis-howto/api-core/hpsearch": "../../api-core-ug.html", - "apis-howto/api-core/overview": "../../api-core-ug.html", - "apis-howto/api-core/metrics": "../../api-core-ug.html", - "apis-howto/api-core/getting-started": "../../api-core-ug.html", - "apis-howto/api-core/distributed": "../../api-core-ug.html", - "concepts/elastic-infrastructure": "../introduction.html", + "apis-howto/api-core/checkpoints": "../../training/apis-howto/api-core-ug.html", + "apis-howto/api-core/hpsearch": "../../training/apis-howto/api-core-ug.html", + "apis-howto/api-core/overview": "../../training/apis-howto/api-core-ug.html", + "apis-howto/api-core/metrics": "../../training/apis-howto/api-core-ug.html", + "apis-howto/api-core/getting-started": "../../training/apis-howto/api-core-ug.html", + "apis-howto/api-core/distributed": "../../training/apis-howto/api-core-ug.html", + "concepts/elastic-infrastructure": "../architecture/introduction.html", "concepts/index": "../architecture/index.html", - "concepts/resource-pool": "../introduction.html", - "concepts/scheduling": "../introduction.html", - "concepts/yaml": "../introduction.html", + "concepts/resource-pool": "../architecture/introduction.html", + "concepts/scheduling": "../architecture/introduction.html", + "concepts/yaml": "../architecture/introduction.html", "examples": "example-solutions/examples.html", "experiment-config": "reference/reference-training/experiment-config-reference.html", - "features/command-notebook-config": "../introduction.html", + "features/command-notebook-config": "../architecture/introduction.html", "features/commands-and-shells": "../interfaces/commands-and-shells.html", - "features/config-template": "../introduction.html", - "features/elastic-infrastructure": "../introduction.html", + "features/config-template": "../architecture/introduction.html", + "features/elastic-infrastructure": "../architecture/introduction.html", "features/experiments": "../training/submit-experiment.html", - "features/index": "../introduction.html", + "features/index": "../architecture/introduction.html", "features/job-queue": "../training/submit-experiment.html", "features/model-registry": "../training/model-management/model-registry-org.html", "features/notebooks": "../interfaces/notebooks.html", - "features/resource-pool": "../introduction.html", - "features/scheduling": "../introduction.html", - "features/system-architecture": "../architecture/index.html", - "features/tensorboard": "../introduction.html", - "features/terminology-concepts": "../introduction.html", - "features/yaml": "../introduction.html", + "features/resource-pool": "../architecture/introduction.html", + "features/scheduling": "../architecture/introduction.html", + "features/system-architecture": "../architecture/system-architecture.html", + "features/tensorboard": "../architecture/introduction.html", + "features/terminology-concepts": "../architecture/introduction.html", + "features/yaml": "../architecture/introduction.html", "getting-started": "quickstart-mdldev.html", "how-to/custom-env": "../training/setup-guide/custom-env.html", "how-to/distributed-training": "../training/dtrain-introduction.html", @@ -180,10 +186,10 @@ def setup(app): "how-to/install-cli": "../interfaces/commands-and-shells.html", "how-to/install-main": "../cluster-setup-guide/basic.html", "how-to/model-debug": "../training/debug-models.html", - "how-to/notebooks": "../introduction.html", + "how-to/notebooks": "../architecture/introduction.html", "how-to/profiling": "../training/dtrain-introduction.html", "how-to/rest-apis": "../reference/rest-api.html", - "how-to/tensorboard": "../introduction.html", + "how-to/tensorboard": "../architecture/introduction.html", "how-to/use-trained-models": "../training/model-management/overview.html", "integrations/ecosystem-integration": "../integrations/ecosystem/ecosystem-integration.html", "integrations/prometheus": "../integrations/prometheus/prometheus", @@ -191,6 +197,7 @@ def setup(app): "interact/cli": "../interfaces/commands-and-shells.html", "interact/index": "../interfaces/commands-and-shells.html", "interact/rest-apis": "../reference/rest-api.html", + "introduction": "../architecture/introduction.html", "join-community": "index.html", "model-hub/index": "../model-hub-library/index.html", "model-hub/mmdetection/api": "../../reference/reference-model-hub/modelhub/mmdetection-api.html", @@ -214,17 +221,17 @@ def setup(app): "reference/api/api-pytorch-samplers": "../../training/apis-howto/api-pytorch-ug.html", "reference/api/api-pytorch": "../../training/apis-howto/api-pytorch-ug.html", "reference/api/api-trial_context": "../../training/apis-howto/overview.html", - "reference/api/command-notebook-config": "../../introduction.html", - "reference/api/config-template": "../../introduction.html", + "reference/api/command-notebook-config": "../../architecture/introduction.html", + "reference/api/config-template": "../../architecture/introduction.html", "reference/api/experiment-config": "../../reference/reference-training/experiment-config-reference.html", "reference/attributions": "../attributions.html", "reference/cli": "../interfaces/commands-and-shells.html", "reference/cluster/cluster-config": "../../reference/reference-deploy/index.html", "reference/cluster-config": "../reference/reference-deploy/index.html", - "reference/cluster/helm-config": "../../sysadmin-deploy-on-k8s/helm-config.html", + "reference/cluster/helm-config": "../reference-deploy/config/helm-config-reference.html", "reference/cluster/historical-cluster-usage-data": "../../cluster-setup-guide/historical-cluster-usage-data.html", - "reference/command-notebook-config": "../introduction.html", - "reference/config-template": "../introduction.html", + "reference/command-notebook-config": "../architecture/introduction.html", + "reference/config-template": "../architecture/introduction.html", "reference/experiment-config": "../reference/reference-training/experiment-config-reference.html", "reference/helm-config": "../reference/reference-deploy/config/helm-config-reference.html", "reference/historical-cluster-usage-data": "../cluster-setup-guide/historical-cluster-usage-data.html", @@ -265,7 +272,7 @@ def setup(app): "sysadmin-deploy-on-prem/index": "../cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-prem/overview.html", "sysadmin-deploy-on-prem/linux-packages": "../cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-prem/linux-packages.html", "sysadmin-deploy-on-prem/requirements": "../cluster-setup-guide/deploy-cluster/sysadmin-deploy-on-prem/requirements.html", - "topic-guides/benefits-of-determined": "../introduction.html", + "topic-guides/benefits-of-determined": "../architecture/introduction.html", "topic-guides/checkpoints": "../training/apis-howto/overview.html", "topic-guides/cluster-configuration/elasticsearch-logging-backend": "../../cluster-setup-guide/elasticsearch-logging-backend.html", "topic-guides/cluster-configuration/oauth": "../../cluster-setup-guide/security/oauth.html", @@ -296,18 +303,18 @@ def setup(app): "topic-guides/oauth": "../cluster-setup-guide/security/oauth.html", "topic-guides/saml": "../cluster-setup-guide/security/saml.html", "topic-guides/scim": "../cluster-setup-guide/security/scim.html", - "topic-guides/system-concepts/elastic-infrastructure": "../../introduction.html", - "topic-guides/system-concepts/resource-pool": "../../introduction.html", - "topic-guides/system-concepts/scheduling": "../../introduction.html", + "topic-guides/system-concepts/elastic-infrastructure": "../../architecture/introduction.html", + "topic-guides/system-concepts/resource-pool": "../../architecture/introduction.html", + "topic-guides/system-concepts/scheduling": "../../architecture/introduction.html", "topic-guides/system-concepts/system-architecture": "../../architecture/index.html", - "topic-guides/system-concepts/terminology-concepts": "../../introduction.html", + "topic-guides/system-concepts/terminology-concepts": "../../architecture/introduction.html", "topic-guides/tls": "../cluster-setup-guide/security/tls.html", "topic-guides/training/experiment-lifecycle": "../../training/submit-experiment.html", "topic-guides/training/reproducibility": "../../training/dtrain-introduction.html", "topic-guides/training/training-units": "../../reference/reference-training/experiment-config-reference.html", "topic-guides/user-interfaces": "../interfaces/commands-and-shells.html", "topic-guides/users": "../cluster-setup-guide/users.html", - "topic-guides/yaml": "../introduction.html", + "topic-guides/yaml": "../architecture/introduction.html", "training-apis/api-core/checkpoints": "../../training/apis-howto/api-core-ug.html", "training-apis/api-core/distributed": "../../training/apis-howto/api-core-ug.html", "training-apis/api-core/getting-started": "../../training/apis-howto/api-core-ug.html", diff --git a/docs/index.rst b/docs/index.rst index 5885b3d406b..6adcf342e30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,33 +1,25 @@ .. toctree:: - :caption: Get Started :hidden: - quickstart-mdldev - Tutorials - Examples - Model Hub Library - System Architecture + Welcome .. toctree:: - :caption: Model Developer Guide + :caption: Getting Started + :maxdepth: 2 :hidden: - Distributed Training - Prepare Container Environment - Prepare Data - Training API Guides - Hyperparameter Tuning - Submit Experiment - How to Debug Models - Model Management - Best Practices + Try Determined + How It Works + quickstart-mdldev + Examples + Model Hub Library .. toctree:: - :caption: Administrator Guide + :caption: Set Up Determined :hidden: + Set Up Guide Basic Setup - Cluster Deployment Security User Accounts Workspaces and Projects @@ -37,10 +29,26 @@ Upgrade Troubleshooting +.. toctree:: + :caption: Model Developer Guide + :hidden: + + Overview + Distributed Training + Prepare Container Environment + Prepare Data + Training API Guides + Hyperparameter Tuning + Submit Experiment + How to Debug Models + Model Management + Best Practices + .. toctree:: :caption: Reference :hidden: + Overview Python SDK REST API Training Reference @@ -57,6 +65,7 @@ WebUI Interface Jupyter Notebooks TensorBoards + Exposing Custom Ports .. toctree:: :caption: Integrations @@ -67,83 +76,72 @@ Prometheus and Grafana attributions -########################## - Determined Documentation -########################## - -************************** - *Welcome to Determined!* -************************** - .. raw:: html -
-

- New features, upgrades, deprecation notices, known issues, and bug fixes: - Release Notes -

-
- -| - -Determined is an open-source deep learning training platform that makes building models fast and -easy. - -With Determined you can: - -- Use state-of-the-art distributed training to train models faster without changing model code. -- Automatically find high-quality models using advanced hyperparameter tuning. -- Get more from your GPUs and reduce cloud GPU costs with preemptible instances and smart - scheduling. -- Leverage experiment tracking out-of-the-box to track and reproduce your work, tracking code - versions, metrics, checkpoints, and hyperparameters. -- Continue using popular deep learning libraries, such as TensorFlow, Keras, and PyTorch by simply - integrating the Determined API with your existing model code. + + + + + + +

Set Up

|

Reference

-Determined integrates these features into an easy-to-use, high-performance deep learning environment -so you can spend your time building models instead of managing infrastructure. +########################## + *Welcome to Determined!* +########################## -| +You can quickly train almost any deep learning model using Determined. .. raw:: html + +| + +.. raw:: html + +
+

+ New features, upgrades, deprecation notices, known issues, and bug fixes: + Release Notes +

+
diff --git a/docs/interfaces/proxy-ports.rst b/docs/interfaces/proxy-ports.rst new file mode 100644 index 00000000000..d521acff909 --- /dev/null +++ b/docs/interfaces/proxy-ports.rst @@ -0,0 +1,65 @@ +.. _proxy-ports: + +####################### + Exposing Custom Ports +####################### + +Determined allows you to expose a custom network port in a task container, and access it using a +local tunnel. + +For multi-container tasks, such as distributed training experiments, only the ports on the chief +container (``rank=0``) will be exposed. + +*************** + Configuration +*************** + +First, specify the ports in the ``environments -> proxy_ports`` section of the experiment or task +config, for example: + +.. code:: yaml + + environment: + proxy_ports: + - proxy_port: 8265 + proxy_tcp: true + +Launch your task or experiment normally. Than, use the ``det`` CLI to start a tunnel. Running the +following command will setup a tunnel proxying ``localhost:8265`` to port ``8265`` in the task +container. + +.. code:: bash + + det -m determined.cli.tunnel --listener 8265 --auth $DET_MASTER $TASK_ID:8265 + +where $DET_MASTER is your Determined master address, and $TASK_ID is the task id of the launched +task or experiment. You can look up the task id using CLI command ``det task list``. + +Alternatively, you can use a shortcut which allows to launch the experiment, follow its logs, and +run the tunnel all at once: + +.. code:: bash + + det e create config_file.yaml model_def -f -p 8265 + +Unauthenticated Mode +==================== + +Optionally, you can run a tunnel with determined authentication turned off. This mode may be useful +when the proxied app is handling security by itself, such as a web app protected by username and +password. To use it, + +#. Add ``unauthenticated: true`` option in the task config. +#. Omit ``--auth`` option from the tunnel CLI. + +.. code:: yaml + + environment: + proxy_ports: + - proxy_port: 8265 + proxy_tcp: true + unauthenticated: true + +.. code:: bash + + det -m determined.cli.tunnel --listener 8265 $DET_MASTER $TASK_ID:8265 diff --git a/docs/interfaces/tensorboard.rst b/docs/interfaces/tensorboard.rst index 24edd5510d4..f45686f04d9 100644 --- a/docs/interfaces/tensorboard.rst +++ b/docs/interfaces/tensorboard.rst @@ -61,7 +61,7 @@ to additional data with a bind-mount. .. code:: yaml environment: - image: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 + image: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 bind_mounts: - host_path: /my/agent/path container_path: /my/container/path diff --git a/docs/reference/overview.rst b/docs/reference/overview.rst new file mode 100644 index 00000000000..2865f247594 --- /dev/null +++ b/docs/reference/overview.rst @@ -0,0 +1,52 @@ +########### + Reference +########### + +.. raw:: html + + diff --git a/docs/reference/reference-deploy/config/helm-config-reference.rst b/docs/reference/reference-deploy/config/helm-config-reference.rst index f437fd337cc..52a8c86813f 100644 --- a/docs/reference/reference-deploy/config/helm-config-reference.rst +++ b/docs/reference/reference-deploy/config/helm-config-reference.rst @@ -173,11 +173,11 @@ - ``cpuImage``: Sets the default docker image for all non-gpu tasks. If a docker image is specified in the :ref:`experiment config ` this default is overriden. - Defaults to: ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12``. + Defaults to: ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1``. - ``gpuImage``: Sets the default docker image for all gpu tasks. If a docker image is specified in the :ref:`experiment config ` this default is overriden. Defaults - to: ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12``. + to: ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1``. - ``enterpriseEdition``: Specifies whether to use Determined enterprise edition. diff --git a/docs/reference/reference-deploy/config/master-config-reference.rst b/docs/reference/reference-deploy/config/master-config-reference.rst index 64782dce819..b4bff0a1a38 100644 --- a/docs/reference/reference-deploy/config/master-config-reference.rst +++ b/docs/reference/reference-deploy/config/master-config-reference.rst @@ -55,9 +55,9 @@ The master supports the following configuration settings: ``cuda`` key (``gpu`` prior to 0.17.6), CPU tasks using ``cpu`` key, and ROCm (AMD GPU) tasks using the ``rocm`` key. Default values: - - ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12`` for NVIDIA GPUs. - - ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12`` for ROCm. - - ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12`` for CPUs. + - ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1`` for NVIDIA GPUs. + - ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.20.1`` for ROCm. + - ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1`` for CPUs. - ``environment_variables``: A list of environment variables that will be set in every task container. Each element of the list should be a string of the form ``NAME=VALUE``. See @@ -296,7 +296,9 @@ The master supports the following configuration settings: - ``user_name``: The username that the Launcher will run as. It is recommended to set this to something other than ``root``. The user must have a home directory with read permissions - for all users to enable access to generated ``sbatch`` scripts and job log files. + for all users to enable access to generated ``sbatch`` scripts and job log files. It must + have access to the Slurm/PBS queue and node status commands (``squeue``, ``sinfo``, + ``pbsnodes``, ``qstat`` ) to discover partitions and to display cluster usage. - ``group_name``: The group that the Launcher will belong to. It should be a group that is not shared with other non-privileged users. @@ -373,6 +375,8 @@ The master supports the following configuration settings: of the workload manager reporting tools that summarize usage by each WCKey/Project value. +.. _cluster-resource-pools: + - ``resource_pools``: A list of resource pools. A resource pool is a collection of identical computational resources. Users can specify which resource pool a job should be assigned to when the job is submitted. Refer to the documentation on :ref:`resource-pools` for more information. diff --git a/docs/reference/reference-interface/job-config-reference.rst b/docs/reference/reference-interface/job-config-reference.rst index 702c91ae867..c9b4ae52ee6 100644 --- a/docs/reference/reference-interface/job-config-reference.rst +++ b/docs/reference/reference-interface/job-config-reference.rst @@ -45,9 +45,9 @@ The following configuration settings are supported: different container images for NVIDIA GPU tasks using ``cuda`` key (``gpu`` prior to 0.17.6), CPU tasks using ``cpu`` key, and ROCm (AMD GPU) tasks using ``rocm`` key. Default values: - - ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12`` for NVIDIA GPUs. - - ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12`` for ROCm. - - ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12`` for CPUs. + - ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1`` for NVIDIA GPUs. + - ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.20.1`` for ROCm. + - ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1`` for CPUs. - ``force_pull_image``: Forcibly pull the image from the Docker registry and bypass the Docker cache. Defaults to ``false``. @@ -79,6 +79,9 @@ The following configuration settings are supported: - ``drop_capabilities``: Just like ``add_capabilities`` but corresponding to the ``--cap-drop`` argument of ``docker run`` rather than ``--cap-add``. + - ``proxy_ports``: Expose configured network ports on the chief task container. See + :ref:`proxy-ports` for details. + - ``resources``: The resources Determined allows a task to use. - ``slots``: Specifies the number of slots to use for the task. The default value is ``1``. The diff --git a/docs/reference/reference-training/experiment-config-reference.rst b/docs/reference/reference-training/experiment-config-reference.rst index 00f6caaded7..a4cfbd3747e 100644 --- a/docs/reference/reference-training/experiment-config-reference.rst +++ b/docs/reference/reference-training/experiment-config-reference.rst @@ -921,6 +921,9 @@ The ``resources`` section defines the resources that an experiment is allowed to slot limit of an active experiment can be changed using ``det experiment set max-slots ``. By default, there is no limit on the number of slots an experiment can use. + When the cluster is deployed with an :ref:`HPC workload manager `, this + value is ignored and instead managed by the configured workload manager. + .. warning:: ``max_slots`` is only considered when scheduling jobs; it is not currently used when @@ -933,6 +936,9 @@ The ``resources`` section defines the resources that an experiment is allowed to weight. The weight of an active experiment can be changed using ``det experiment set weight ``. The default weight is ``1``. + When the cluster is deployed with an :ref:`HPC workload manager `, this + value is ignored and instead managed by the configured workload manager. + ``shm_size`` The size of ``/dev/shm`` for task containers. The value can be a number in bytes or a number with a suffix (e.g., ``128M`` for 128MiB or ``1.5G`` for 1.5GiB). Defaults to ``4294967296`` (4GiB). @@ -945,6 +951,9 @@ The ``resources`` section defines the resources that an experiment is allowed to values. If using Kubernetes, the opposite is true; experiments with higher priorities are scheduled before those with lower priorities. Refer to :ref:`scheduling` for more information. + When the cluster is deployed with an :ref:`HPC workload manager `, this + value is ignored and instead managed by the configured workload manager. + ``resource_pool`` The resource pool where this experiment will be scheduled. If no resource pool is specified, experiments will run in the default GPU pool. Refer to :ref:`resource-pools` for more @@ -1035,9 +1044,9 @@ workloads for this experiment. For more information on customizing the trial env images for NVIDIA GPU tasks using ``cuda`` key (``gpu`` prior to 0.17.6), CPU tasks using ``cpu`` key, and ROCm (AMD GPU) tasks using ``rocm`` key. Default values: - - ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12`` for NVIDIA GPUs. - - ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12`` for CPUs. - - ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12`` for ROCm. + - ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1`` for NVIDIA GPUs. + - ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1`` for CPUs. + - ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.20.1`` for ROCm. When the cluster is configured with :ref:`resource_manager.type: slurm ` and ``container_run_type: singularity``, images are executed using @@ -1115,6 +1124,9 @@ workloads for this experiment. For more information on customizing the trial env Just like ``add_capabilities`` but corresponding to the ``--cap-drop`` argument of ``docker run`` rather than ``--cap-add``. +``proxy_ports``: Expose configured network ports on the chief task container. See :ref:`proxy-ports` +for details. + *************** Optimizations *************** diff --git a/docs/release-notes/nuke-columns.rst b/docs/release-notes/nuke-columns.rst new file mode 100644 index 00000000000..5883f0e9fe4 --- /dev/null +++ b/docs/release-notes/nuke-columns.rst @@ -0,0 +1,8 @@ +:orphan: + +**Breaking Changes** + +- Database: Several unused columns have been dropped from ``raw_steps``, ``raw_validations``, + ``raw_checkpoints`` database tables. The database migration will involve a sequential scan for + these tables, and it may take significant amount of time, depending on the database size and + performance. diff --git a/docs/release-notes/nuke-exp-compare.rst b/docs/release-notes/nuke-exp-compare.rst new file mode 100644 index 00000000000..4c607294bb4 --- /dev/null +++ b/docs/release-notes/nuke-exp-compare.rst @@ -0,0 +1,6 @@ +:orphan: + +**Removed Features** + +- API: Remove internal ``ExpCompareMetricNames``, ``ExpCompareTrialsSample`` endpoints, which have + been unused and deprecated since 0.19.5. diff --git a/docs/release-notes/proxy.rst b/docs/release-notes/proxy.rst new file mode 100644 index 00000000000..6e4d88f8808 --- /dev/null +++ b/docs/release-notes/proxy.rst @@ -0,0 +1,7 @@ +:orphan: + +**New Features** + +- Added an ability for tasks or experiments to expose arbitrary ports that can be tunneled to the + using the CLI. See :ref:`proxy-ports` for more details or an example at + ``examples/features/ports``. diff --git a/docs/release-notes/pt-images.rst b/docs/release-notes/pt-images.rst new file mode 100644 index 00000000000..ee21a694424 --- /dev/null +++ b/docs/release-notes/pt-images.rst @@ -0,0 +1,9 @@ +:orphan: + +**New Features** + +- Container Images: Add maintained images for PyTorch-only environments. + + - Current environment images contain both PyTorch and TensorFlow which results in large image + sizes. Adding a class of images for users who do not require TensorFlow but may still + require TensorBoard. diff --git a/docs/release-notes/rbac-ntsc-mr.rst b/docs/release-notes/rbac-ntsc-mr.rst new file mode 100644 index 00000000000..e1cc1441762 --- /dev/null +++ b/docs/release-notes/rbac-ntsc-mr.rst @@ -0,0 +1,18 @@ +:orphan: + +**New Features** + +- RBAC: Following on the initial RBAC support added in 0.19.7 the enterprise edition of Determined + (`HPE Machine Learning Development Environment + `_) + has added support for Role-Based Access Control over new entities: + + - JupyterLab Notebooks, Tensorboards, Shells, and Commands are now housed under workspaces. + Access to these tasks can now be restricted by role. Launching Tensorboards from WebUI + launches at the experiment worspace by default. + + - Model Registry: models are now associated with workspaces. Models can be moved between + workspaces and access to them can be restricted by role. + + These changes allow for more granular control over who can access what resources. See :ref:`rbac` + for more information. diff --git a/docs/requirements.txt b/docs/requirements.txt index 367d5ed3600..f5ea84dfc8c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,13 +1,14 @@ # pin docutils at 0.15.2 to avoid transitive dependency conflict with botocore (requires < 0.16) docutils==0.15.2 pillow -rstfmt==0.0.10 +rstfmt==0.0.12 # Plugins sphinx-reredirects>=0.0.1 sphinx-copybutton>=0.4.0 sphinx-sitemap>=2.2.0 sphinx==4.2.0 +myst_parser # Theme furo==2021.10.9 diff --git a/docs/training/apis-howto/overview.rst b/docs/training/apis-howto/overview.rst index 49f8aa8760b..03f1f6c59ae 100644 --- a/docs/training/apis-howto/overview.rst +++ b/docs/training/apis-howto/overview.rst @@ -1,6 +1,6 @@ -##################### - Training API Guides -##################### +############### + Training APIs +############### You can train almost any deep learning model using the Determined Training APIs. The Training API guides describe how to take your existing model code and train your model in Determined. Each API @@ -70,15 +70,15 @@ Determined supports both TensorFlow 1 and 2. The version of TensorFlow that is u experiment is controlled by the container image that has been configured for that experiment. Determined provides prebuilt Docker images that include TensorFlow 2.8, 1.15, and 2.7, respectively: -- ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12`` (default) -- ``determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.12`` -- ``determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-0.19.12`` +- ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1`` (default) +- ``determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.20.1`` +- ``determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-0.20.1`` We also provide lightweight CPU-only counterparts: -- ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12`` -- ``determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-0.19.12`` -- ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-0.19.12`` +- ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1`` +- ``determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-0.20.1`` +- ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-0.20.1`` To change the container image used for an experiment, specify :ref:`environment.image ` in the experiment configuration file. Please see :ref:`container-images` @@ -94,7 +94,7 @@ images. Determined has experimental support for ROCm. Determined provides a prebuilt Docker image that includes ROCm 4.2, PyTorch 1.9 and Tensorflow 2.5: -- ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12`` +- ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.20.1`` Known limitations: diff --git a/docs/training/index.rst b/docs/training/index.rst new file mode 100644 index 00000000000..168941a1b1a --- /dev/null +++ b/docs/training/index.rst @@ -0,0 +1,64 @@ +####################### + Model Developer Guide +####################### + +.. raw:: html + + diff --git a/docs/training/setup-guide/custom-env.rst b/docs/training/setup-guide/custom-env.rst index fc12cb0e249..4b9b42bae3d 100644 --- a/docs/training/setup-guide/custom-env.rst +++ b/docs/training/setup-guide/custom-env.rst @@ -101,11 +101,11 @@ Default Images +-------------+-----------------------------------------------------------------------------------+ | Environment | File Name | +=============+===================================================================================+ -| CPUs | ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12`` | +| CPUs | ``determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1`` | +-------------+-----------------------------------------------------------------------------------+ -| Nvidia GPUs | ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12`` | +| Nvidia GPUs | ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1`` | +-------------+-----------------------------------------------------------------------------------+ -| AMD GPUs | ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12`` | +| AMD GPUs | ``determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.20.1`` | +-------------+-----------------------------------------------------------------------------------+ .. _custom-docker-images: @@ -132,7 +132,7 @@ Example Dockerfile that installs custom ``conda``-, ``pip``-, and ``apt``-based .. code:: bash # Determined Image - FROM determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 + FROM determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 # Custom Configuration RUN apt-get update && \ @@ -195,7 +195,7 @@ environments using :ref:`custom images `: .. code:: bash # Determined Image - FROM determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12 + FROM determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1 # Create a virtual environment RUN conda create -n myenv python=3.8 diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst index f292c1ecf7f..08354ed686d 100644 --- a/docs/tutorials/index.rst +++ b/docs/tutorials/index.rst @@ -1,20 +1,16 @@ -########### - Tutorials -########### +################ + Try Determined +################ Learn the basics of working with Determined and how to port your existing code to the Determined environment. -************************** - Get Started in 5 Minutes -************************** +************ + Quickstart +************ -+---------------------------------+--------------------------------------------------------------+ -| Title | Description | -+=================================+==============================================================+ -| :doc:`pytorch-mnist-local-qs` | In a few steps, learn how to run your first experiment in | -| | Determined using only a single CPU or GPU. | -+---------------------------------+--------------------------------------------------------------+ +To get started with your first experiment, visit the :doc:`Quickstart for Model Developers +<../quickstart-mdldev>`. ****************************** Get Started with a Trial API @@ -35,14 +31,16 @@ environment. | | port a ``tf.keras`` model to Determined. | +---------------------------------+--------------------------------------------------------------+ -Go Further -========== +********************************************** + Want to Learn About a Specific Training API? +********************************************** -Visit the :doc:`Training API Guides
` for in-depth guides that -describe how to take your existing model code and train your model in Determined. +:doc:`Training API Guides
` describe how to take your existing model +code and train your model in Determined. -Looking for Examples? -===================== +*********************** + Looking for Examples? +*********************** Examples let you build off of an existing model that already runs on Determined. Visit our :doc:`Examples
` to see if the model you'd like to train is already diff --git a/e2e_tests/tests/cluster/test_proxy.py b/e2e_tests/tests/cluster/test_proxy.py new file mode 100644 index 00000000000..3207c41193c --- /dev/null +++ b/e2e_tests/tests/cluster/test_proxy.py @@ -0,0 +1,183 @@ +import csv +import os +import pathlib +import re +import subprocess +import time +from io import StringIO + +import pytest +import requests + +from determined.common.api import bindings +from tests import api_utils +from tests import config as conf +from tests import experiment as exp + + +def _experiment_task_id(exp_id: int) -> str: + sess = api_utils.determined_test_session() + trials = bindings.get_GetExperimentTrials(sess, experimentId=exp_id).trials + assert len(trials) > 0 + + trial = trials[0] + task_id = trial.taskId + assert task_id is not None + + return task_id + + +def _probe_tunnel(proc: "subprocess.Popen[str]") -> None: + max_tunnel_ticks = 300 + for i in range(max_tunnel_ticks): + try: + r = requests.get("http://localhost:8265", timeout=5) + if r.status_code == 200: + break + except requests.exceptions.ConnectionError: + pass + except requests.exceptions.ReadTimeout: + pass + if i + 1 % 10 == 0: + print(f"Tunnel probe pending: {i} ticks...") + time.sleep(1) + if proc.poll() is not None: + pytest.fail(f"Tunnel process has exited prematurely, return code: {proc.returncode}") + else: + pytest.fail(f"Failed to probe the tunnel in {max_tunnel_ticks} ticks") + + print(f"Tunnel probe done after {i} ticks.") + + +def _ray_job_submit(exp_path: pathlib.Path) -> None: + env = os.environ.copy() + env["RAY_ADDRESS"] = "http://localhost:8265" + subprocess.run( + [ + "ray", + "job", + "submit", + "--working-dir", + str(exp_path), + "--", + "python", + "ray_job.py", + ], + check=True, + env=env, + ) + + +@pytest.mark.e2e_cpu +def test_experiment_proxy_ray_tunnel() -> None: + exp_path = conf.EXAMPLES_PATH / "features" / "ports" + exp_id = exp.create_experiment( + str(exp_path / "ray_launcher.yaml"), + str(exp_path), + ["--config", "max_restarts=0", "--config", "resources.slots=1"], + ) + try: + exp.wait_for_experiment_state(exp_id, bindings.experimentv1State.STATE_RUNNING) + task_id = _experiment_task_id(exp_id) + + proc = subprocess.Popen( + [ + "python", + "-m", + "determined.cli.tunnel", + "--listener", + "8265", + "--auth", + conf.make_master_url(), + f"{task_id}:8265", + ], + text=True, + ) + + try: + _probe_tunnel(proc) + _ray_job_submit(exp_path) + finally: + proc.kill() + finally: + sess = api_utils.determined_test_session() + bindings.post_KillExperiment(sess, id=exp_id) + + +def _parse_exp_id(proc: "subprocess.Popen[str]") -> int: + assert proc.stdout is not None + for line in iter(proc.stdout.readline, ""): + if proc.poll() is not None: + pytest.fail( + f"Unexpected `det e create` failure before receiving an experiment id, " + f"return code: f{proc.returncode}" + ) + m = re.search(r"Created experiment (\d+)\n", line) + if m is not None: + return int(m.group(1)) + pytest.fail("Failed to find experiment id in `det e create` output") + + +def _kill_all_ray_experiments() -> None: + proc = subprocess.run( + [ + "det", + "-m", + conf.make_master_url(), + "experiment", + "list", + "--csv", + ], + capture_output=True, + text=True, + check=True, + ) + reader = csv.DictReader(StringIO(proc.stdout)) + sess = api_utils.determined_test_session() + for row in reader: + if row["name"] == "ray_launcher": + if row["state"] not in ["CANCELED", "COMPLETED"]: + exp_id = int(row["ID"]) + bindings.post_KillExperiment(sess, id=exp_id) + + +@pytest.mark.e2e_cpu +def test_experiment_proxy_ray_publish() -> None: + exp_path = conf.EXAMPLES_PATH / "features" / "ports" + proc = subprocess.Popen( + [ + "det", + "-m", + conf.make_master_url(), + "experiment", + "create", + str(exp_path / "ray_launcher.yaml"), + str(exp_path), + "--config", + "max_restarts=0", + "--config", + "resources.slots=1", + "-f", + "-p", + "8265", + ], + stdout=subprocess.PIPE, + text=True, + ) + + try: + try: + exp_id = _parse_exp_id(proc) + except Exception: + _kill_all_ray_experiments() + raise + + try: + exp.wait_for_experiment_state(exp_id, bindings.experimentv1State.STATE_RUNNING) + _probe_tunnel(proc) + _ray_job_submit(exp_path) + finally: + sess = api_utils.determined_test_session() + bindings.post_KillExperiment(sess, id=exp_id) + finally: + proc.kill() diff --git a/e2e_tests/tests/config.py b/e2e_tests/tests/config.py index aaaad3bc333..bf7cc81ac89 100644 --- a/e2e_tests/tests/config.py +++ b/e2e_tests/tests/config.py @@ -13,18 +13,23 @@ MAX_TRIAL_BUILD_SECS = 90 -DEFAULT_TF1_CPU_IMAGE = "determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-ad0591c" -DEFAULT_TF2_CPU_IMAGE = "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c" -DEFAULT_TF1_GPU_IMAGE = "determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-ad0591c" -DEFAULT_TF2_GPU_IMAGE = "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c" +DEFAULT_TF1_CPU_IMAGE = "determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-7aa5364" +DEFAULT_TF2_CPU_IMAGE = "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364" +DEFAULT_TF1_GPU_IMAGE = "determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-7aa5364" +DEFAULT_TF2_GPU_IMAGE = "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364" +DEFAULT_PT_CPU_IMAGE = "determinedai/environments:py-3.8-pytorch-1.12-cpu-7aa5364" +DEFAULT_PT_GPU_IMAGE = "determinedai/environments:cuda-11.3-pytorch-1.12-gpu-7aa5364" TF1_CPU_IMAGE = os.environ.get("TF1_CPU_IMAGE") or DEFAULT_TF1_CPU_IMAGE TF2_CPU_IMAGE = os.environ.get("TF2_CPU_IMAGE") or DEFAULT_TF2_CPU_IMAGE TF1_GPU_IMAGE = os.environ.get("TF1_GPU_IMAGE") or DEFAULT_TF1_GPU_IMAGE TF2_GPU_IMAGE = os.environ.get("TF2_GPU_IMAGE") or DEFAULT_TF2_GPU_IMAGE +PT_CPU_IMAGE = os.environ.get("PT_CPU_IMAGE") or DEFAULT_PT_CPU_IMAGE +PT_GPU_IMAGE = os.environ.get("PT_GPU_IMAGE") or DEFAULT_PT_GPU_IMAGE GPU_ENABLED = os.environ.get("DET_TEST_GPU_ENABLED", "1") not in ("0", "false") PROJECT_ROOT_PATH = Path(__file__).resolve().parents[2] +EXAMPLES_PATH = PROJECT_ROOT_PATH / "examples" def fixtures_path(path: str) -> str: @@ -163,6 +168,10 @@ def set_tf2_image(config: Dict[Any, Any]) -> Dict[Any, Any]: return set_image(config, TF2_CPU_IMAGE, TF2_GPU_IMAGE) +def set_pt_image(config: Dict[Any, Any]) -> Dict[Any, Any]: + return set_image(config, PT_CPU_IMAGE, PT_GPU_IMAGE) + + def set_random_seed(config: Dict[Any, Any], seed: int) -> Dict[Any, Any]: config = config.copy() config.setdefault("reproducibility", {}) diff --git a/e2e_tests/tests/deploy/test_local.py b/e2e_tests/tests/deploy/test_local.py index 5a0bcec37bb..fa32e158604 100644 --- a/e2e_tests/tests/deploy/test_local.py +++ b/e2e_tests/tests/deploy/test_local.py @@ -26,8 +26,10 @@ def det_deploy(subcommand: List) -> None: subprocess.run(command) -def cluster_up(arguments: List) -> None: +def cluster_up(arguments: List, delete_db: bool = True) -> None: command = ["cluster-up", "--no-gpu"] + if delete_db: + command += ["--delete-db"] det_version = conf.DET_VERSION if det_version is not None: command += ["--det-version", det_version] @@ -41,8 +43,10 @@ def cluster_down(arguments: List) -> None: det_deploy(command) -def master_up(arguments: List) -> None: +def master_up(arguments: List, delete_db: bool = True) -> None: command = ["master-up"] + if delete_db: + command += ["--delete-db"] det_version = conf.DET_VERSION if det_version is not None: command += ["--det-version", det_version] @@ -283,7 +287,8 @@ def test_stress_agents_reconnect(steps: int, num_agents: int, should_disconnect: agent_up(["--agent-name", f"agent-{i}"], fluent_offset=i) time.sleep(10) - for _ in range(steps): + for step in range(steps): + print("================ step", step) for agent_id, agent_is_up in enumerate(agents_are_up): if random.choice([True, False]): # Flip agents status randomly. continue @@ -302,6 +307,7 @@ def test_stress_agents_reconnect(steps: int, num_agents: int, should_disconnect: else: agent_enable([f"agent-{agent_id}"]) agents_are_up[agent_id] = True + print("agents_are_up:", agents_are_up) time.sleep(10) # Validate that our master kept track of the agent reconnect spam. @@ -315,8 +321,10 @@ def test_stress_agents_reconnect(steps: int, num_agents: int, should_disconnect: ] ).decode() ) + print("agent_list:", agent_list) assert sum(agents_are_up) <= len(agent_list) for agent in agent_list: + print("agent:", agent) agent_id = int(agent["id"].replace("agent-", "")) if agents_are_up[agent_id] != agent["enabled"]: p = subprocess.run( diff --git a/e2e_tests/tests/experiment/__init__.py b/e2e_tests/tests/experiment/__init__.py index dcaa1c76eef..77dc3c2d833 100644 --- a/e2e_tests/tests/experiment/__init__.py +++ b/e2e_tests/tests/experiment/__init__.py @@ -3,6 +3,7 @@ assert_equivalent_trials, assert_performed_final_checkpoint, assert_performed_initial_validation, + cancel_experiment, cancel_single, cancel_trial, check_if_string_present_in_trial_logs, diff --git a/e2e_tests/tests/experiment/experiment.py b/e2e_tests/tests/experiment/experiment.py index 7657cf4919f..88683638256 100644 --- a/e2e_tests/tests/experiment/experiment.py +++ b/e2e_tests/tests/experiment/experiment.py @@ -299,11 +299,7 @@ def experiment_has_completed_workload(experiment_id: int) -> bool: for t in trials: for s in t.workloads: - if ( - s.training is not None and s.training.state == experimentv1State.STATE_COMPLETED - ) or ( - s.validation is not None and s.validation.state == experimentv1State.STATE_COMPLETED - ): + if s.training is not None or s.validation is not None: return True return False @@ -506,7 +502,6 @@ def assert_performed_initial_validation(exp_id: int) -> None: zeroth_step = workloads_with_validation(workloads)[0] assert zeroth_step.totalBatches == 0 - assert zeroth_step.state == experimentv1State.STATE_COMPLETED def last_workload_matches_last_checkpoint( @@ -524,7 +519,6 @@ def last_workload_matches_last_checkpoint( last_workload_detail = last_workload.training or last_workload.validation assert last_workload_detail is not None assert last_workload_detail.totalBatches == last_checkpoint.totalBatches - assert last_workload_detail.state == experimentv1State.STATE_COMPLETED elif last_workload.checkpoint: last_checkpoint_detail = last_workload.checkpoint assert last_checkpoint_detail is not None @@ -744,10 +738,8 @@ def verify_completed_experiment_metadata( for s in t.workloads: if s.training: batch_ids.append(s.training.totalBatches) - assert s.training.state == experimentv1State.STATE_COMPLETED if s.validation: batch_ids.append(s.validation.totalBatches) - assert s.validation.state == experimentv1State.STATE_COMPLETED if s.checkpoint: batch_ids.append(s.checkpoint.totalBatches) assert s.checkpoint.state in { diff --git a/e2e_tests/tests/experiment/test_pytorch.py b/e2e_tests/tests/experiment/test_pytorch.py index e7cd8531c09..ab480097c49 100644 --- a/e2e_tests/tests/experiment/test_pytorch.py +++ b/e2e_tests/tests/experiment/test_pytorch.py @@ -1,3 +1,4 @@ +import warnings from typing import Callable, List import pytest @@ -9,13 +10,24 @@ @pytest.mark.e2e_gpu @pytest.mark.parametrize("aggregation_frequency", [1, 4]) +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) def test_pytorch_11_const( - aggregation_frequency: int, using_k8s: bool, collect_trial_profiles: Callable[[int], None] + aggregation_frequency: int, + image_type: str, + using_k8s: bool, + collect_trial_profiles: Callable[[int], None], ) -> None: config = conf.load_config(conf.fixtures_path("mnist_pytorch/const-pytorch11.yaml")) config = conf.set_aggregation_frequency(config, aggregation_frequency) config = conf.set_profiling_enabled(config) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + if using_k8s: pod_spec = { "metadata": {"labels": {"ci": "testing"}}, @@ -39,10 +51,18 @@ def test_pytorch_11_const( @pytest.mark.e2e_cpu -def test_pytorch_load(collect_trial_profiles: Callable[[int], None]) -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_pytorch_load(image_type: str, collect_trial_profiles: Callable[[int], None]) -> None: config = conf.load_config(conf.fixtures_path("mnist_pytorch/const-pytorch11.yaml")) config = conf.set_profiling_enabled(config) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + experiment_id = exp.run_basic_test_with_temp_config( config, conf.tutorials_path("mnist_pytorch"), 1 ) @@ -58,7 +78,8 @@ def test_pytorch_load(collect_trial_profiles: Callable[[int], None]) -> None: @pytest.mark.e2e_cpu -def test_pytorch_const_warm_start() -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_pytorch_const_warm_start(image_type: str) -> None: """ Test that specifying an earlier trial checkpoint to warm-start from correctly populates the later trials' `warm_start_checkpoint_id` fields. @@ -66,6 +87,13 @@ def test_pytorch_const_warm_start() -> None: config = conf.load_config(conf.tutorials_path("mnist_pytorch/const.yaml")) config = conf.set_max_length(config, {"batches": 200}) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + experiment_id1 = exp.run_basic_test_with_temp_config( config, conf.tutorials_path("mnist_pytorch"), 1 ) @@ -118,12 +146,22 @@ def test_pytorch_const_with_amp( @pytest.mark.parallel -def test_pytorch_cifar10_parallel(collect_trial_profiles: Callable[[int], None]) -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_pytorch_cifar10_parallel( + image_type: str, collect_trial_profiles: Callable[[int], None] +) -> None: config = conf.load_config(conf.cv_examples_path("cifar10_pytorch/const.yaml")) config = conf.set_max_length(config, {"batches": 200}) config = conf.set_slots_per_trial(config, 8) config = conf.set_profiling_enabled(config) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + experiment_id = exp.run_basic_test_with_temp_config( config, conf.cv_examples_path("cifar10_pytorch"), 1 ) @@ -139,12 +177,22 @@ def test_pytorch_cifar10_parallel(collect_trial_profiles: Callable[[int], None]) @pytest.mark.parallel -def test_pytorch_gan_parallel(collect_trial_profiles: Callable[[int], None]) -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_pytorch_gan_parallel( + image_type: str, collect_trial_profiles: Callable[[int], None] +) -> None: config = conf.load_config(conf.gan_examples_path("gan_mnist_pytorch/const.yaml")) config = conf.set_max_length(config, {"batches": 200}) config = conf.set_slots_per_trial(config, 8) config = conf.set_profiling_enabled(config) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + experiment_id = exp.run_basic_test_with_temp_config( config, conf.gan_examples_path("gan_mnist_pytorch"), 1 ) diff --git a/e2e_tests/tests/experiment/test_tasks_raw_csv.py b/e2e_tests/tests/experiment/test_tasks_raw_csv.py new file mode 100644 index 00000000000..e17b00ce1db --- /dev/null +++ b/e2e_tests/tests/experiment/test_tasks_raw_csv.py @@ -0,0 +1,98 @@ +import csv +import re +from datetime import datetime, timezone +from io import StringIO + +import pytest +import requests + +from determined.common import api +from determined.common.api.bindings import experimentv1State +from tests import cluster as clu +from tests import command as cmd +from tests import config as conf +from tests import experiment as exp + + +# Create a No_Op experiment and Check training/validation times +@pytest.mark.e2e_cpu +def test_experiment_capture() -> None: + start_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + experiment_id = exp.create_experiment( + conf.fixtures_path("no_op/single.yaml"), conf.fixtures_path("no_op") + ) + exp.wait_for_experiment_state(experiment_id, experimentv1State.STATE_COMPLETED) + + end_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + r = api.get( + conf.make_master_url(), + f"/resources/allocation/tasks-raw?timestamp_after={start_time}×tamp_before={end_time}", + ) + assert r.status_code == requests.codes.ok, r.text + + # Check if a trial entry exists for experiment that just ran + reader = csv.DictReader(StringIO(r.text)) + matches = [row for row in reader if int(row["experiment_id"]) == experiment_id] + assert len(matches) >= 1 + + +@pytest.mark.e2e_cpu +def test_notebook_capture() -> None: + start_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + task_id = None + with cmd.interactive_command("notebook", "start") as notebook: + task_id = notebook.task_id + + for line in notebook.stdout: + if re.search("Jupyter Notebook .*is running at", line) is not None: + return + assert task_id is not None + + end_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + r = api.get( + conf.make_master_url(), + f"/resources/allocation/tasks-raw?timestamp_after={start_time}×tamp_before={end_time}", + ) + assert r.status_code == requests.codes.ok, r.text + + assert re.search(f"{task_id},NOTEBOOK", r.text) is not None + + +# Create a No_Op Experiment/Tensorboard & Confirm Tensorboard task is captured +@pytest.mark.e2e_cpu +def test_tensorboard_experiment_capture() -> None: + start_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + experiment_id = exp.create_experiment( + conf.fixtures_path("no_op/single.yaml"), conf.fixtures_path("no_op") + ) + + exp.wait_for_experiment_state(experiment_id, experimentv1State.STATE_COMPLETED) + + task_id = None + with cmd.interactive_command("tensorboard", "start", "--detach", str(experiment_id)) as tb: + task_id = tb.task_id + for line in tb.stdout: + if "TensorBoard is running at: http" in line: + break + if "TensorBoard is awaiting metrics" in line: + raise AssertionError("Tensorboard did not find metrics") + assert task_id is not None + clu.utils.wait_for_task_state("tensorboard", task_id, "TERMINATED") + + end_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + r = api.get( + conf.make_master_url(), + f"/resources/allocation/tasks-raw?timestamp_after={start_time}×tamp_before={end_time}", + ) + assert r.status_code == requests.codes.ok, r.text + + # Confirm Experiment is captured and valid + reader = csv.DictReader(StringIO(r.text)) + matches = [row for row in reader if int(row["experiment_id"]) == experiment_id] + assert len(matches) >= 1 + + # Confirm Tensorboard task is captured + assert re.search(f"{task_id},TENSORBOARD", r.text) is not None diff --git a/e2e_tests/tests/fixtures/pytorch_lightning_amp/apex_amp.yaml b/e2e_tests/tests/fixtures/pytorch_lightning_amp/apex_amp.yaml index 1b792cff1e7..3346f4e096b 100644 --- a/e2e_tests/tests/fixtures/pytorch_lightning_amp/apex_amp.yaml +++ b/e2e_tests/tests/fixtures/pytorch_lightning_amp/apex_amp.yaml @@ -14,6 +14,6 @@ searcher: entrypoint: apex_amp_model_def:MNistApexAMPTrial environment: image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 - cpu: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12 + gpu: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 + cpu: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1 diff --git a/e2e_tests/tests/fixtures/pytorch_lightning_amp/auto_amp.yaml b/e2e_tests/tests/fixtures/pytorch_lightning_amp/auto_amp.yaml index 2d035aa754d..d2d40ce9baf 100644 --- a/e2e_tests/tests/fixtures/pytorch_lightning_amp/auto_amp.yaml +++ b/e2e_tests/tests/fixtures/pytorch_lightning_amp/auto_amp.yaml @@ -14,6 +14,6 @@ searcher: entrypoint: auto_amp_model_def:MNistAutoAMPTrial environment: image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 - cpu: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12 + gpu: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 + cpu: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1 diff --git a/e2e_tests/tests/nightly/test_capability.py b/e2e_tests/tests/nightly/test_capability.py index a8871c22337..bd43a97c7c9 100644 --- a/e2e_tests/tests/nightly/test_capability.py +++ b/e2e_tests/tests/nightly/test_capability.py @@ -55,7 +55,6 @@ def test_efficientdet_coco_pytorch_const() -> None: @pytest.mark.nightly def test_detectron2_coco_pytorch_const() -> None: - pytest.skip("Temporarily skipping this until we update the detectron2 example.") config = conf.load_config(conf.cv_examples_path("detectron2_coco_pytorch/const_fake.yaml")) config = conf.set_max_length(config, {"batches": 200}) diff --git a/e2e_tests/tests/nightly/test_convergence.py b/e2e_tests/tests/nightly/test_convergence.py index 1a4d60b7866..874ff8f30e3 100644 --- a/e2e_tests/tests/nightly/test_convergence.py +++ b/e2e_tests/tests/nightly/test_convergence.py @@ -304,7 +304,7 @@ def test_hf_trainer_api_accuracy() -> None: if step.get("validation") ] - target_accuracy = 0.83 + target_accuracy = 0.82 assert max(validation_accuracies) > target_accuracy, ( "hf_trainer_api did not reach minimum target accuracy {} in {} steps." " full validation accuracy history: {}".format( diff --git a/e2e_tests/tests/nightly/test_distributed.py b/e2e_tests/tests/nightly/test_distributed.py index 39eb3b0b5aa..be51c3bc87f 100644 --- a/e2e_tests/tests/nightly/test_distributed.py +++ b/e2e_tests/tests/nightly/test_distributed.py @@ -1,6 +1,7 @@ import os import shutil import tempfile +import warnings import pytest @@ -9,10 +10,18 @@ @pytest.mark.distributed -def test_mnist_pytorch_distributed() -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_mnist_pytorch_distributed(image_type: str) -> None: config = conf.load_config(conf.tutorials_path("mnist_pytorch/distributed.yaml")) config = conf.set_max_length(config, {"batches": 200}) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + exp.run_basic_test_with_temp_config(config, conf.tutorials_path("mnist_pytorch"), 1) @@ -39,10 +48,18 @@ def test_imagenet_pytorch_distributed() -> None: @pytest.mark.distributed -def test_cifar10_pytorch_distributed() -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_cifar10_pytorch_distributed(image_type: str) -> None: config = conf.load_config(conf.cv_examples_path("cifar10_pytorch/distributed.yaml")) config = conf.set_max_length(config, {"batches": 200}) + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + exp.run_basic_test_with_temp_config(config, conf.cv_examples_path("cifar10_pytorch"), 1) @@ -149,12 +166,20 @@ def test_deformabledetr_coco_pytorch_distributed() -> None: @pytest.mark.distributed -def test_word_language_transformer_distributed() -> None: +@pytest.mark.parametrize("image_type", ["PT", "TF2"]) +def test_word_language_transformer_distributed(image_type: str) -> None: config = conf.load_config(conf.nlp_examples_path("word_language_model/distributed.yaml")) config = conf.set_max_length(config, {"batches": 200}) config = config.copy() config["hyperparameters"]["model_cls"] = "Transformer" + if image_type == "PT": + config = conf.set_pt_image(config) + elif image_type == "TF2": + config = conf.set_tf2_image(config) + else: + warnings.warn("Using default images", stacklevel=2) + exp.run_basic_test_with_temp_config(config, conf.nlp_examples_path("word_language_model"), 1) diff --git a/e2e_tests/tests/requirements.txt b/e2e_tests/tests/requirements.txt index 424ad4cd113..9644e24c3b0 100644 --- a/e2e_tests/tests/requirements.txt +++ b/e2e_tests/tests/requirements.txt @@ -1,4 +1,6 @@ appdirs +# TODO(MLG-336): remove this requirement when tensorflow-macos is upgraded. +protobuf<3.20; sys_platform == 'darwin' and platform_machine == 'arm64' # pytest 6.0 has linter-breaking changes pytest>=6.0.1 pytest-timeout @@ -17,3 +19,4 @@ kubernetes # when pytest executes e2e_tests. numpy>=1.20 h5py>=3 +ray[default] diff --git a/examples/computer_vision/detectron2_coco_pytorch/.detignore b/examples/computer_vision/detectron2_coco_pytorch/.detignore new file mode 100644 index 00000000000..94143827ed0 --- /dev/null +++ b/examples/computer_vision/detectron2_coco_pytorch/.detignore @@ -0,0 +1 @@ +Dockerfile diff --git a/examples/computer_vision/detectron2_coco_pytorch/Dockerfile b/examples/computer_vision/detectron2_coco_pytorch/Dockerfile new file mode 100644 index 00000000000..b9ace7d924b --- /dev/null +++ b/examples/computer_vision/detectron2_coco_pytorch/Dockerfile @@ -0,0 +1,20 @@ +FROM determinedai/environments:cuda-10.2-base-gpu-0.20.1 + +RUN pip install tensorboard cmake onnx # cmake from apt-get is too old +RUN pip install torch==1.10 torchvision==0.11.1 -f https://download.pytorch.org/whl/cu101/torch_stable.html + +RUN pip install 'git+https://github.com/facebookresearch/fvcore' +# install detectron2 +RUN git clone https://github.com/facebookresearch/detectron2 detectron2_repo +# set FORCE_CUDA because during `docker build` cuda is not accessible +ENV FORCE_CUDA="1" +# This will by default build detectron2 for all common cuda architectures and take a lot more time, +# because inside `docker build`, there is no way to tell which architecture will be used. +#ARG TORCH_CUDA_ARCH_LIST="Kepler;Kepler+Tesla;Maxwell;Maxwell+Tegra;Pascal;Volta;Turing" +ARG TORCH_CUDA_ARCH_LIST="Kepler;Kepler+Tesla" +ENV TORCH_CUDA_ARCH_LIST="${TORCH_CUDA_ARCH_LIST}" + +RUN pip install -e detectron2_repo + +RUN pip install horovod==0.24.2 + diff --git a/examples/computer_vision/detectron2_coco_pytorch/Makefile b/examples/computer_vision/detectron2_coco_pytorch/Makefile new file mode 100644 index 00000000000..0aefc76d51a --- /dev/null +++ b/examples/computer_vision/detectron2_coco_pytorch/Makefile @@ -0,0 +1,6 @@ +TAG := determinedai/example-detectron2:0.6-cuda-10.2-pytorch-1.10 + +.PHONY: build +build: + docker build -f Dockerfile -t $(TAG) . && \ + docker push $(TAG) diff --git a/examples/computer_vision/detectron2_coco_pytorch/README.md b/examples/computer_vision/detectron2_coco_pytorch/README.md index 8e3575d69b6..b69bbe180bc 100644 --- a/examples/computer_vision/detectron2_coco_pytorch/README.md +++ b/examples/computer_vision/detectron2_coco_pytorch/README.md @@ -1,21 +1,20 @@ # Detectron2 Example -This example is an Detectron2 port using Determined's PyTorchTrial API. The original example can be found on - [Facebook Research's Detectron2 Github](https://github.com/facebookresearch/detectron2/blob/v0.1.2/tools/plain_train_net.py). This example is based on version 1.2 to compare training performance. More information on the original benchmarks can be found at the benchmark [documentation] (https://detectron2.readthedocs.io/en/latest/notes/benchmarks.html) page. +This example is a port of Detectron2 using Determined's PyTorchTrial API. The original example can be found on + [Facebook Research's Detectron2 Github](https://github.com/facebookresearch/detectron2/blob/v0.6/tools/plain_train_net.py). More information on the original benchmarks can be found at the benchmark [documentation] (https://detectron2.readthedocs.io/en/latest/notes/benchmarks.html) page. ## Files * **model_def.py**: The core code for the model. This includes building and compiling the model. -* **startup-hook.sh**: This script will automatically be run by Determined during startup of every container launched for this experiment. This script exports the approprate dataset location . * **detectron2_files/**: This folder includes original Detectron2 files that have been slightly altered to work with Determined. ### Configuration Files * **const.yaml**: Train the model with constant hyperparameter values. * **distributed.yaml**: Same as `const.yaml`, but trains the model with multiple GPUs (distributed training). - +* **const_fake.yaml**: This is used in Determined's automated test workflows and can be ignored. ## Data -The coco dataset is the primary dataset for this port. Information on how to download the dataset can be found [here](https://cocodataset.org/#home). Once downloaded, copy the data on all the Determined agents. Then configure the bind mount in the configuration files to the appropriate location. More information on bind mounts can be found [here](https://docs.determined.ai/latest/tutorials/data-access.html#distributed-file-system). +The Common Objects in Context (COCO) dataset is the primary dataset for this port. Information on how to download the dataset can be found [here](https://cocodataset.org/#home). Once downloaded to the host machine, configure the bind mount in the experiment configuration files to point to the location. More information on bind mounts can be found [here](https://docs.determined.ai/latest/tutorials/data-access.html#distributed-file-system). ## To Run If you have not yet installed Determined, installation instructions can be found @@ -26,4 +25,8 @@ const.yaml .`. The other configurations can be run by specifying the appropriate configuration file in place of `const.yaml`. ## Results -The default configurations are based on [the original Detectron benchmarks](https://github.com/facebookresearch/detectron2/blob/v0.1.2/configs/Detectron1-Comparisons/faster_rcnn_R_50_FPN_noaug_1x.yaml). At the end of training, the boxAP and and segmAP should be over 34.00. \ No newline at end of file +The default configurations are based on [the original Detectron benchmarks](https://github.com/facebookresearch/detectron2/blob/v0.6/configs/Detectron1-Comparisons/faster_rcnn_R_50_FPN_noaug_1x.yaml). At the end of training, the boxAP and and segmAP should be over 34.00. + +## Environment +We provide a [Docker image](https://hub.docker.com/r/determinedai/example-detectron2) with CUDA 10.1, PyTorch 1.10, other Determined dependencies, and Detectron2 0.6 - it is partially based on the [Detectron2 Dockerfile](https://github.com/facebookresearch/detectron2/blob/v0.6/docker/Dockerfile), but notably with OpenCV omitted. + diff --git a/examples/computer_vision/detectron2_coco_pytorch/const.yaml b/examples/computer_vision/detectron2_coco_pytorch/const.yaml index 1fce96f6f4c..55897ebf1b3 100644 --- a/examples/computer_vision/detectron2_coco_pytorch/const.yaml +++ b/examples/computer_vision/detectron2_coco_pytorch/const.yaml @@ -1,6 +1,6 @@ name: detectron2_const environment: - image: "katport/detectron2:cuda-10.1" + image: "determinedai/example-detectron2:0.6-cuda-10.2-pytorch-1.10" environment_variables: - DETECTRON2_DATASETS=/mnt/dtrain-fsx/detectron2 hyperparameters: @@ -22,4 +22,4 @@ bind_mounts: container_path: /mnt/dtrain-fsx/detectron2 read_only: true min_validation_period: - batches: 5000 \ No newline at end of file + batches: 5000 diff --git a/examples/computer_vision/detectron2_coco_pytorch/const_fake.yaml b/examples/computer_vision/detectron2_coco_pytorch/const_fake.yaml index e3403e4274a..9a0fc5163b7 100644 --- a/examples/computer_vision/detectron2_coco_pytorch/const_fake.yaml +++ b/examples/computer_vision/detectron2_coco_pytorch/const_fake.yaml @@ -1,6 +1,6 @@ name: detectron2_const_e2e_tests environment: - image: "katport/detectron2:e2e-cuda-10.1" + image: "determinedai/example-detectron2:0.6-cuda-10.2-pytorch-1.10" environment_variables: - DETECTRON2_DATASETS=. hyperparameters: @@ -17,4 +17,4 @@ searcher: resources: slots_per_trial: 1 entrypoint: model_def:DetectronTrial -max_restarts: 0 \ No newline at end of file +max_restarts: 0 diff --git a/examples/computer_vision/detectron2_coco_pytorch/distributed.yaml b/examples/computer_vision/detectron2_coco_pytorch/distributed.yaml index 232cba6aa8b..12ebc71377e 100644 --- a/examples/computer_vision/detectron2_coco_pytorch/distributed.yaml +++ b/examples/computer_vision/detectron2_coco_pytorch/distributed.yaml @@ -1,6 +1,6 @@ name: detectron2_distributed environment: - image: "katport/detectron2:cuda-10.1" + image: "determinedai/example-detectron2:0.6-cuda-10.2-pytorch-1.10" environment_variables: - DETECTRON2_DATASETS=/mnt/dtrain-fsx/detectron2 hyperparameters: @@ -23,4 +23,4 @@ bind_mounts: container_path: /mnt/dtrain-fsx/detectron2 read_only: true min_validation_period: - batches: 5000 \ No newline at end of file + batches: 5000 diff --git a/examples/computer_vision/detectron2_coco_pytorch/model_def.py b/examples/computer_vision/detectron2_coco_pytorch/model_def.py index d80e4b77698..479115ae012 100644 --- a/examples/computer_vision/detectron2_coco_pytorch/model_def.py +++ b/examples/computer_vision/detectron2_coco_pytorch/model_def.py @@ -8,6 +8,7 @@ from detectron2.config import get_cfg from detectron2.modeling import build_model from detectron2.solver import build_lr_scheduler, build_optimizer +from detectron2.utils.events import EventStorage from detectron2_files.common import * from detectron2_files.data import * from detectron2_files.evaluator import * @@ -73,8 +74,8 @@ def build_validation_data_loader(self): return data_loader def train_batch(self, batch: TorchData, epoch_idx: int, batch_idx: int): - - loss_dict = self.model(batch) + with EventStorage(): + loss_dict = self.model(batch) losses = sum(loss_dict.values()) losses_reduced = sum(loss for loss in loss_dict.values()) diff --git a/examples/computer_vision/efficientdet_pytorch/const.yaml b/examples/computer_vision/efficientdet_pytorch/const.yaml index 090cd0abb1c..3792de061f7 100644 --- a/examples/computer_vision/efficientdet_pytorch/const.yaml +++ b/examples/computer_vision/efficientdet_pytorch/const.yaml @@ -1,6 +1,6 @@ description: efficientdet_const environment: - image: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.12 + image: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.20.1 hyperparameters: global_batch_size: 16 min_loss_scale: 16.0 diff --git a/examples/computer_vision/efficientdet_pytorch/const_fake.yaml b/examples/computer_vision/efficientdet_pytorch/const_fake.yaml index d65a75d5a72..3a384f756ca 100644 --- a/examples/computer_vision/efficientdet_pytorch/const_fake.yaml +++ b/examples/computer_vision/efficientdet_pytorch/const_fake.yaml @@ -1,6 +1,6 @@ description: efficientdet_const environment: - image: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.12 + image: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.20.1 hyperparameters: global_batch_size: 16 min_loss_scale: 16.0 diff --git a/examples/computer_vision/unets_tf_keras/const.yaml b/examples/computer_vision/unets_tf_keras/const.yaml index fda0d914014..233deed7592 100644 --- a/examples/computer_vision/unets_tf_keras/const.yaml +++ b/examples/computer_vision/unets_tf_keras/const.yaml @@ -22,4 +22,4 @@ min_validation_period: entrypoint: model_def:UNetsTrial scheduling_unit: 57 environment: - image: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c + image: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 diff --git a/examples/computer_vision/unets_tf_keras/distributed.yaml b/examples/computer_vision/unets_tf_keras/distributed.yaml index 617288a0d81..6a4ea76ce34 100644 --- a/examples/computer_vision/unets_tf_keras/distributed.yaml +++ b/examples/computer_vision/unets_tf_keras/distributed.yaml @@ -25,4 +25,4 @@ min_validation_period: scheduling_unit: 8 entrypoint: model_def:UNetsTrial environment: - image: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c + image: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 diff --git a/examples/decision_trees/gbt_titanic_estimator/adaptive.yaml b/examples/decision_trees/gbt_titanic_estimator/adaptive.yaml index 4fbcba9c92f..6b8fbca6c44 100644 --- a/examples/decision_trees/gbt_titanic_estimator/adaptive.yaml +++ b/examples/decision_trees/gbt_titanic_estimator/adaptive.yaml @@ -42,4 +42,4 @@ searcher: entrypoint: model_def:BoostedTreesTrial scheduling_unit: 1 environment: - image: "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12" + image: "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1" diff --git a/examples/decision_trees/gbt_titanic_estimator/const.yaml b/examples/decision_trees/gbt_titanic_estimator/const.yaml index baafe6a08c3..a1f9272b9b4 100644 --- a/examples/decision_trees/gbt_titanic_estimator/const.yaml +++ b/examples/decision_trees/gbt_titanic_estimator/const.yaml @@ -20,4 +20,4 @@ searcher: entrypoint: model_def:BoostedTreesTrial scheduling_unit: 1 environment: - image: "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12" + image: "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1" diff --git a/examples/deepspeed/cifar10_cpu_offloading/zero_3_cpu_offload.yaml b/examples/deepspeed/cifar10_cpu_offloading/zero_3_cpu_offload.yaml index 7a729ae0e83..02c097497d5 100644 --- a/examples/deepspeed/cifar10_cpu_offloading/zero_3_cpu_offload.yaml +++ b/examples/deepspeed/cifar10_cpu_offloading/zero_3_cpu_offload.yaml @@ -14,7 +14,7 @@ environment: # You may need to modify this to match your network configuration. - NCCL_SOCKET_IFNAME=ens,eth,ib image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c + gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-7aa5364 bind_mounts: - host_path: /tmp container_path: /data diff --git a/examples/deepspeed/cifar10_cpu_offloading/zero_no_offload.yaml b/examples/deepspeed/cifar10_cpu_offloading/zero_no_offload.yaml index 67e8520cb57..2a0d643513c 100644 --- a/examples/deepspeed/cifar10_cpu_offloading/zero_no_offload.yaml +++ b/examples/deepspeed/cifar10_cpu_offloading/zero_no_offload.yaml @@ -14,7 +14,7 @@ environment: # You may need to modify this to match your network configuration. - NCCL_SOCKET_IFNAME=ens,eth,ib image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c + gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-7aa5364 bind_mounts: - host_path: /tmp container_path: /data diff --git a/examples/deepspeed/cifar10_moe/moe.yaml b/examples/deepspeed/cifar10_moe/moe.yaml index 547e9096ed2..b222416e4d8 100644 --- a/examples/deepspeed/cifar10_moe/moe.yaml +++ b/examples/deepspeed/cifar10_moe/moe.yaml @@ -21,7 +21,7 @@ environment: # - NCCL_BLOCKING_WAIT=1 # - NCCL_IB_DISABLE=1 image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c + gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-7aa5364 bind_mounts: - host_path: /tmp container_path: /data diff --git a/examples/deepspeed/cifar10_moe/zero_stages.yaml b/examples/deepspeed/cifar10_moe/zero_stages.yaml index bd3606bb4de..b2d092fc325 100644 --- a/examples/deepspeed/cifar10_moe/zero_stages.yaml +++ b/examples/deepspeed/cifar10_moe/zero_stages.yaml @@ -22,7 +22,7 @@ environment: # - NCCL_BLOCKING_WAIT=1 # - NCCL_IB_DISABLE=1 image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c + gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-7aa5364 bind_mounts: - host_path: /tmp container_path: /data diff --git a/examples/deepspeed/gpt_neox/Dockerfile b/examples/deepspeed/gpt_neox/Dockerfile index 08f6bdbed63..d031536854b 100644 --- a/examples/deepspeed/gpt_neox/Dockerfile +++ b/examples/deepspeed/gpt_neox/Dockerfile @@ -1,4 +1,4 @@ -FROM determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-ad0591c +FROM determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-7aa5364 # Install deepspeed & dependencies RUN apt-get install -y mpich @@ -10,4 +10,6 @@ RUN git clone -b determined https://github.com/determined-ai/gpt-neox.git RUN python gpt-neox/megatron/fused_kernels/setup.py install RUN pip install -r gpt-neox/requirements/requirements.txt -RUN cd gpt-neox; python prepare_data.py -d ./data +RUN gsutil cp -r gs://determined-ai-public-datasets/text_data /gpt-neox && mv /gpt-neox/text_data /gpt-neox/data +RUN chmod -R 777 /gpt-neox +RUN chmod -R 777 /tmp diff --git a/examples/deepspeed/gpt_neox/startup-hook.sh b/examples/deepspeed/gpt_neox/startup-hook.sh index b354e8c8ccd..128f0e8504f 100644 --- a/examples/deepspeed/gpt_neox/startup-hook.sh +++ b/examples/deepspeed/gpt_neox/startup-hook.sh @@ -1,6 +1,8 @@ export PYTHONPATH=$PYTHONPATH:/gpt-neox # Copy dataset from docker image to shared filesystem +USER=$(whoami) +mkdir /tmp/${USER} mkdir -p /run/determined/workdir/shared_fs/data cp -r -n /gpt-neox/data /run/determined/workdir/shared_fs/ diff --git a/examples/deepspeed/pipeline_parallelism/distributed.yaml b/examples/deepspeed/pipeline_parallelism/distributed.yaml index 373e183c3ab..c58a5e12394 100644 --- a/examples/deepspeed/pipeline_parallelism/distributed.yaml +++ b/examples/deepspeed/pipeline_parallelism/distributed.yaml @@ -20,7 +20,7 @@ environment: # - CUDA_LAUNCH_BLOCKING=1 # - NCCL_BLOCKING_WAIT=1 image: - gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c + gpu: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-7aa5364 resources: slots_per_trial: 2 records_per_epoch: 50000 diff --git a/examples/features/ports/README.md b/examples/features/ports/README.md new file mode 100644 index 00000000000..e62d066219d --- /dev/null +++ b/examples/features/ports/README.md @@ -0,0 +1,30 @@ +# Running a Ray Cluster Within a Determined Task + +## Launching a Cluster + +To launch a single worker cluster, and expose Ray dashboard port 8265 on `localhost:8265`: + + det e create ray_launcher.yaml . -f -p 8265 --config resources.slots_per_trial=1 + +Note: this experiment will run forever, it must be explicitly terminated either in WebUI or using CLI ``det e kill EXP_ID`` when you no longer need it. + +Running a test job: + + pip install -U "ray[air]" + export RAY_ADDRESS="http://localhost:8265"; ray job submit --working-dir . -- python ray_job.py + + +## Advanced Usage + +### Multi-Worker Cluster + +To launch a multi-worker cluster with 4 total workers: + + det e create ray_launcher.yaml . -f -p 8265 --config resources.slots_per_trial=4 + +### Using Different Local Port for Ray Dashboard Proxy + +By default, this example binds local port 8265 and proxies it to ray dashboard running within a Determined task. To use a different port e.g. 8266: + + det e create ray_launcher.yaml . -f -p 8266:8265 --config resources.slots_per_trial=1 + export RAY_ADDRESS="http://localhost:8266"; ray job submit --working-dir . -- python ray_job.py diff --git a/examples/features/ports/ray_job.py b/examples/features/ports/ray_job.py new file mode 100644 index 00000000000..6c0e1b31a79 --- /dev/null +++ b/examples/features/ports/ray_job.py @@ -0,0 +1,7 @@ +import numpy as np +import ray + +ds = ray.data.range(5) +result = ds.map(lambda x: x * 2).take_all() +print("Result:", result) +assert result[-1] == 8 diff --git a/examples/features/ports/ray_launcher.py b/examples/features/ports/ray_launcher.py new file mode 100644 index 00000000000..01423c09ddc --- /dev/null +++ b/examples/features/ports/ray_launcher.py @@ -0,0 +1,29 @@ +import logging +import subprocess +import sys +import time + +import determined as det + + +def launcher_main(cross_rank, chief_ip): + if cross_rank == 0: + subprocess.run(["ray", "start", "--head", "--dashboard-host", "0.0.0.0"], check=True) + else: + subprocess.run(["ray", "start", f"--address={chief_ip}:6379"], check=True) + while True: + time.sleep(1) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format=det.LOG_FORMAT) + + info = det.get_cluster_info() + assert info is not None, "this example only runs on-cluster" + slots_per_node = len(info.slot_ids) + num_nodes = len(info.container_addrs) + cross_rank = info.container_rank + chief_ip = info.container_addrs[0] + + exitcode = launcher_main(cross_rank, chief_ip) + sys.exit(exitcode) diff --git a/examples/features/ports/ray_launcher.yaml b/examples/features/ports/ray_launcher.yaml new file mode 100644 index 00000000000..4997e153f74 --- /dev/null +++ b/examples/features/ports/ray_launcher.yaml @@ -0,0 +1,19 @@ +name: ray_launcher +entrypoint: python3 ray_launcher.py + +resources: + slots_per_trial: 1 + +searcher: + name: single + metric: x + max_length: 10000000 + +max_restarts: 0 + +environment: + proxy_ports: + - proxy_port: 6379 + proxy_tcp: true + - proxy_port: 8265 + proxy_tcp: true diff --git a/examples/features/ports/startup-hook.sh b/examples/features/ports/startup-hook.sh new file mode 100755 index 00000000000..cb7a1b5ae9a --- /dev/null +++ b/examples/features/ports/startup-hook.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pip install -U "ray[air]" diff --git a/examples/graphs/proteins_pytorch_geometric/adaptive.yaml b/examples/graphs/proteins_pytorch_geometric/adaptive.yaml index 6093426b114..09621eb1506 100644 --- a/examples/graphs/proteins_pytorch_geometric/adaptive.yaml +++ b/examples/graphs/proteins_pytorch_geometric/adaptive.yaml @@ -32,4 +32,4 @@ searcher: entrypoint: model_def:GraphConvTrial environment: image: - cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c + cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 diff --git a/examples/graphs/proteins_pytorch_geometric/const.yaml b/examples/graphs/proteins_pytorch_geometric/const.yaml index 10c17108910..e24fa59cc62 100644 --- a/examples/graphs/proteins_pytorch_geometric/const.yaml +++ b/examples/graphs/proteins_pytorch_geometric/const.yaml @@ -18,4 +18,4 @@ searcher: entrypoint: model_def:GraphConvTrial environment: image: - cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c + cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 diff --git a/examples/graphs/proteins_pytorch_geometric/distributed.yaml b/examples/graphs/proteins_pytorch_geometric/distributed.yaml index e41aed865f0..cab14adf952 100644 --- a/examples/graphs/proteins_pytorch_geometric/distributed.yaml +++ b/examples/graphs/proteins_pytorch_geometric/distributed.yaml @@ -18,6 +18,6 @@ searcher: entrypoint: model_def:GraphConvTrial environment: image: - cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c + cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 resources: slots_per_trial: 4 diff --git a/examples/tests/requirements.txt b/examples/tests/requirements.txt index cdef20b7ecc..3046affb334 100644 --- a/examples/tests/requirements.txt +++ b/examples/tests/requirements.txt @@ -1,4 +1,6 @@ # pytest 6.0 has linter-breaking changes +# TODO(MLG-336): remove this requirement when tensorflow-macos is upgraded. +protobuf<3.20; sys_platform == 'darwin' and platform_machine == 'arm64' pytest>=6.0.1 tensorflow==2.8.4; sys_platform != 'darwin' or platform_machine != 'arm64' tensorflow-macos==2.8.0; sys_platform == 'darwin' and platform_machine == 'arm64' diff --git a/harness/determined/__init__.py b/harness/determined/__init__.py index e441892a10e..97384ea4937 100644 --- a/harness/determined/__init__.py +++ b/harness/determined/__init__.py @@ -1,6 +1,7 @@ from determined.__version__ import __version__ from determined._experiment_config import ExperimentConfig from determined._info import RendezvousInfo, TrialInfo, ResourcesInfo, ClusterInfo, get_cluster_info +from determined._import import import_from_path from determined import core from determined._env_context import EnvContext from determined._trial_context import TrialContext diff --git a/harness/determined/__version__.py b/harness/determined/__version__.py index 35775c59a65..d4f7707f462 100644 --- a/harness/determined/__version__.py +++ b/harness/determined/__version__.py @@ -1 +1 @@ -__version__ = "0.19.12-dev0" +__version__ = "0.20.1-dev0" diff --git a/harness/determined/_import.py b/harness/determined/_import.py new file mode 100644 index 00000000000..c43b074181c --- /dev/null +++ b/harness/determined/_import.py @@ -0,0 +1,135 @@ +import contextlib +import os +import sys +from importlib import machinery +from typing import Iterator, Set, no_type_check + + +class NoCachePathFinder(machinery.PathFinder): + """Prevent __pycache__ files from being added to the checkpoint.""" + + @no_type_check + def find_spec(self, name, path=None, target=None): + spec = super().find_spec(name, path, target) + if spec is not None and spec.loader is not None: + old_loader = spec.loader + + class NoCacheLoader(type(old_loader)): # type: ignore + def __init__(wrapper) -> None: + pass + + def set_data(wrapper, *args, **kwarg): + # We don't want to generate __pycache__ + # directories in the checkpoint dir. + raise NotImplementedError() + + def __getattr__(wrapper, *args, **kwargs): + return getattr(old_loader, *args, **kwargs) + + def __setattr__(wrapper, *args, **kwargs): + return setattr(old_loader, *args, **kwargs) + + def __delattr__(wrapper, *args, **kwargs): + return delattr(old_loader, *args, **kwargs) + + spec.loader = NoCacheLoader() + + return spec + + +def modules_from_dir(path: str) -> Set[str]: + """List any sys.modules that were imported from a given path.""" + abspath = os.path.abspath(path) + keys_to_pop = set() + # sort items before processing, so we always see x before x.y before x.y.z + for k, v in sorted(sys.modules.items()): + if v is None: + # cached miss in sys.modules + keys_to_pop.add(k) + continue + + if getattr(v, "__file__", None) is None: + # non-file module, ignore + continue + + # Look for dir containing the module. + dirname, basename = os.path.split(v.__file__) + if basename == "__init__.py": + dirname, basename = os.path.split(dirname) + + if dirname == abspath or dirname in keys_to_pop: + # module was imported from path, or is a submodule of such a module + keys_to_pop.add(k) + return keys_to_pop + + +_in_import_from_path = False + + +@contextlib.contextmanager +def import_from_path(path: os.PathLike) -> Iterator: + """ + import_from_path allows you to import from a specific directory and cleans up afterwards. + + Even if you are importing identically-named files, you can import them as separate modules. + This is intended to help when you have, for example, a current model_def.py, but also import an + older model_def.py from a checkpoint into the same interpreter, without conflicts (so long as + you import them as different names, of course). + + Example: + + .. code:: + + import model_def as new_model_def + + with det.import_from_path(checkpoint_dir): + import model_def as old_model_def + + old_model = old_model_def.my_build_model() + old_model.my_load_weights(checkpoint_dir) + + current_model = new_model_def.my_build_model( + base_layers=old_model.base_layers + ) + + Without ``import_from_path``, the above code snippet would hit issues where ``model_def`` had + already been imported so the second ``import`` would have been a noop and both ``new_model_def`` + and ``old_model_def` would represent the same underlying module. + """ + + global _in_import_from_path + if _in_import_from_path: + raise RuntimeError( + "det.import_from_path unfortunately does not support nesting or calling from multiple " + "threads." + ) + + fspath = os.fspath(path) + old_sys_path = sys.path + popped_modules = {} + for localpath in ("", os.getcwd()): + if localpath in sys.path: + # Remove "" from sys.path. + sys.path = [p for p in sys.path if p != ""] + # Remove anything imported from local directory + for m in modules_from_dir(localpath): + popped_modules[m] = sys.modules.pop(m) + # Inject path at front of sys.path. + sys.path = [os.path.abspath(path)] + sys.path + # Include a Finder that prevents found files from creating __pycache__ files. + old_meta_path = sys.meta_path + sys.meta_path = [NoCachePathFinder()] + sys.meta_path # type: ignore + _in_import_from_path = True + try: + yield + finally: + _in_import_from_path = False + # Restore meta path. + sys.meta_path = old_meta_path + # Remove cached modules loaded from path. + for m in modules_from_dir(fspath): + del sys.modules[m] + # Restore the original sys.path. + sys.path = old_sys_path + # Restore local directory modules to sys.modules. + sys.modules.update(popped_modules) diff --git a/harness/determined/cli/__init__.py b/harness/determined/cli/__init__.py index 4048972b12d..33313bae9b0 100644 --- a/harness/determined/cli/__init__.py +++ b/harness/determined/cli/__init__.py @@ -5,7 +5,6 @@ setup_session, require_feature_flag, login_sdk_client, - report_cli_error, print_warnings, ) from determined.cli import ( diff --git a/harness/determined/cli/_util.py b/harness/determined/cli/_util.py index b71894cf133..86b2cbc2a77 100644 --- a/harness/determined/cli/_util.py +++ b/harness/determined/cli/_util.py @@ -1,7 +1,7 @@ import argparse import functools import sys -from typing import Any, Callable, Dict, List, Sequence, Union +from typing import Any, Callable, Dict, List, Sequence import termcolor @@ -115,11 +115,6 @@ def wrapper(args: argparse.Namespace) -> None: return decorator -def report_cli_error(msg: Union[str, Exception]) -> None: - print(termcolor.colored(f"Error: {msg}", "red"), file=sys.stderr) - sys.exit(1) - - def print_warnings(warnings: Sequence[bindings.v1LaunchWarning]) -> None: for warning in warnings: print(termcolor.colored(api.WARNING_MESSAGE_MAP[warning], "yellow"), file=sys.stderr) diff --git a/harness/determined/cli/agent.py b/harness/determined/cli/agent.py index 4e2b0589ccb..299d1b3c1ed 100644 --- a/harness/determined/cli/agent.py +++ b/harness/determined/cli/agent.py @@ -9,6 +9,7 @@ from determined import cli from determined.cli import render from determined.cli import task as cli_task +from determined.cli.errors import CliError from determined.common import api from determined.common.api import authentication, bindings from determined.common.check import check_false @@ -145,8 +146,7 @@ def patch(args: argparse.Namespace) -> None: check_false(args.all and args.agent_id) if not (args.all or args.agent_id): - print("Error: must specify exactly one of `--all` or agent_id", file=sys.stderr) - sys.exit(1) + raise CliError("Must specify exactly on of --all or --agent-id") if args.agent_id: agent_ids = [args.agent_id] diff --git a/harness/determined/cli/cli.py b/harness/determined/cli/cli.py index b52e0cb49b9..93d50b8b95e 100644 --- a/harness/determined/cli/cli.py +++ b/harness/determined/cli/cli.py @@ -3,7 +3,13 @@ import socket import ssl import sys -from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, FileType, Namespace +from argparse import ( + ArgumentDefaultsHelpFormatter, + ArgumentError, + ArgumentParser, + FileType, + Namespace, +) from typing import List, Sequence, Union, cast import argcomplete @@ -18,6 +24,7 @@ from determined.cli import render from determined.cli.agent import args_description as agent_args_description from determined.cli.checkpoint import args_description as checkpoint_args_description +from determined.cli.dev import args_description as dev_args_description from determined.cli.experiment import args_description as experiment_args_description from determined.cli.job import args_description as job_args_description from determined.cli.master import args_description as master_args_description @@ -52,7 +59,7 @@ ) from determined.errors import EnterpriseOnlyError -from .errors import FeatureFlagDisabled +from .errors import CliError, FeatureFlagDisabled @authentication.required @@ -159,6 +166,7 @@ def render_sequence(sequence: List[str]) -> str: + workspace_args_description + auth_args_description + oauth_args_description + + dev_args_description ) @@ -192,13 +200,13 @@ def main( parsed_args = parser.parse_args(args) - def die(message: str, always_print_traceback: bool = False) -> None: + def die(message: str, always_print_traceback: bool = False, exit_code: int = 1) -> None: if always_print_traceback or debug_mode(): import traceback traceback.print_exc(file=sys.stderr) - parser.exit(1, colored(message + "\n", "red")) + parser.exit(exit_code, colored(message + "\n", "red")) v = vars(parsed_args) if not v.get("func"): @@ -280,6 +288,10 @@ def die(message: str, always_print_traceback: bool = False) -> None: die(f"Determined Enterprise Edition is required for this functionality: {e}") except FeatureFlagDisabled as e: die(f"Master does not support this operation: {e}") + except CliError as e: + die(e.message, exit_code=e.exit_code) + except ArgumentError as e: + die(e.message, exit_code=2) except Exception: die("Failed to {}".format(parsed_args.func.__name__), always_print_traceback=True) except KeyboardInterrupt: diff --git a/harness/determined/cli/command.py b/harness/determined/cli/command.py index f31a33f84e1..6f1abbaf6a1 100644 --- a/harness/determined/cli/command.py +++ b/harness/determined/cli/command.py @@ -1,7 +1,7 @@ import base64 import json import re -from argparse import Namespace +from argparse import ArgumentError, Namespace from collections import OrderedDict, namedtuple from pathlib import Path from typing import IO, Any, Dict, Iterable, List, Optional, Tuple, Union @@ -199,7 +199,7 @@ def list_tasks(args: Namespace) -> None: cli.setup_session(args), args.workspace_name ) if workspace is None: - return cli.report_cli_error(f'Workspace "{args.workspace_name}" not found.') + raise ArgumentError(None, f'Workspace "{args.workspace_name}" not found.') params["workspaceId"] = workspace.id diff --git a/harness/determined/cli/dev.py b/harness/determined/cli/dev.py new file mode 100644 index 00000000000..d6e5c5ea503 --- /dev/null +++ b/harness/determined/cli/dev.py @@ -0,0 +1,84 @@ +import argparse +import json +import shlex +import shutil +import subprocess +import sys +from argparse import Namespace +from typing import Any, List + +from termcolor import colored + +from determined.common.api import authentication +from determined.common.api.request import make_url +from determined.common.declarative_argparse import Arg, Cmd + + +@authentication.required +def token(_: Namespace) -> None: + token = authentication.must_cli_auth().get_session_token() + print(token) + + +@authentication.required +def curl(args: Namespace) -> None: + assert authentication.cli_auth is not None + if shutil.which("curl") is None: + print(colored("curl is not installed on this machine", "red")) + sys.exit(1) + + cmd: List[str] = [ + "curl", + make_url(args.master, args.path), + "-H", + f"Authorization: Bearer {authentication.cli_auth.get_session_token()}", + "-s", + ] + if args.curl_args: + cmd += args.curl_args + + if args.x: + if hasattr(shlex, "join"): # added in py 3.8 + print(shlex.join(cmd)) # type: ignore + else: + print(" ".join(shlex.quote(arg) for arg in cmd)) + output = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + if output.stderr: + print(output.stderr.decode("utf8"), file=sys.stderr) + + out = output.stdout.decode("utf8") + try: + json_resp = json.loads(out) + if shutil.which("jq") is not None: + subprocess.run(["jq", "."], input=out, text=True) + else: + print(json.dumps(json_resp, indent=4)) + except json.decoder.JSONDecodeError: + print(out) + + sys.exit(output.returncode) + + +args_description = [ + Cmd( + "dev", + None, + argparse.SUPPRESS, + [ + Cmd("auth-token", token, "print the active user's auth token", []), + Cmd( + "curl", + curl, + "invoke curl", + [ + Arg( + "-x", help="display the curl command that will be run", action="store_true" + ), + Arg("path", help="path to curl (e.g. /api/v1/experiments?x=z)"), + Arg("curl_args", nargs=argparse.REMAINDER, help="curl arguments"), + ], + ), + ], + ), +] # type: List[Any] diff --git a/harness/determined/cli/errors.py b/harness/determined/cli/errors.py index 74f2514daaa..103c44565ac 100644 --- a/harness/determined/cli/errors.py +++ b/harness/determined/cli/errors.py @@ -1,3 +1,6 @@ +from typing import Optional + + class FeatureFlagDisabled(Exception): """ Exception indicating that there is a currently disabled feature flag @@ -5,3 +8,25 @@ class FeatureFlagDisabled(Exception): """ pass + + +class CliError(Exception): + """ + Base class for all CLI errors. + """ + + name: str + + def __init__( + self, message: str, e_stack: Optional[Exception] = None, exit_code: int = 1 + ) -> None: + """ + Args: + - e_stack: The exception that triggered this error. + - exit_code: The exit code to use when exiting the CLI. + """ + super().__init__(message) + self.name = "Error" + self.exit_code = exit_code + self.e_stack = e_stack + self.message = message diff --git a/harness/determined/cli/experiment.py b/harness/determined/cli/experiment.py index f5f4a0f5f7d..81cdcc6bef2 100644 --- a/harness/determined/cli/experiment.py +++ b/harness/determined/cli/experiment.py @@ -5,7 +5,7 @@ import pathlib import sys import time -from argparse import FileType, Namespace +from argparse import ArgumentError, FileType, Namespace from pathlib import Path from pprint import pformat from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union @@ -17,8 +17,9 @@ import determined.experimental import determined.load from determined import cli -from determined.cli import checkpoint, render +from determined.cli import checkpoint, proxy, render from determined.cli.command import CONFIG_DESC, parse_config_overrides +from determined.cli.errors import CliError from determined.common import api, context, set_logger, util, yaml from determined.common.api import authentication, bindings, logs from determined.common.declarative_argparse import Arg, Cmd, Group @@ -65,8 +66,7 @@ def read_git_metadata(model_def_path: pathlib.Path) -> Tuple[str, str, str, str] try: from git import Repo except ImportError as e: # pragma: no cover - print(f"Error: Please verify that git is installed correctly: {e}") - sys.exit(1) + raise CliError(f"Error: Please verify that git is installed correctly: {e}") if model_def_path.is_dir(): repo_path = model_def_path.resolve() @@ -74,27 +74,24 @@ def read_git_metadata(model_def_path: pathlib.Path) -> Tuple[str, str, str, str] repo_path = model_def_path.parent.resolve() if not repo_path.joinpath(".git").is_dir(): - print( + raise CliError( f"Error: No git directory found at {repo_path}. Please " "initialize a git repository or refrain from " "using the --git feature." ) - sys.exit(1) try: repo = Repo(str(repo_path)) except Exception as e: - print(f"Failed to initialize git repository at {repo_path}: {e}") - sys.exit(1) + raise CliError(f"Failed to initialize git repository at {repo_path}: {e}") if repo.is_dirty(): - print( + raise CliError( "Git working directory is dirty. Please commit the " "following changes before creating an experiment " "with the --git feature:\n" + f"\n{repo.git.status()}" ) - print(repo.git.status()) - sys.exit(1) commit = repo.commit() commit_hash = commit.hexsha @@ -113,8 +110,7 @@ def read_git_metadata(model_def_path: pathlib.Path) -> Tuple[str, str, str, str] remote_url = repo.git.config(f"remote.{remote_name}.url", get=True) print(f"Using remote URL '{remote_url}' from upstream branch '{upstream_branch}'") except Exception as e: - print("Failed to find the upstream branch: ", e) - sys.exit(1) + raise CliError("Failed to find the upstream branch: ", e) return (remote_url, commit_hash, committer, commit_date) @@ -125,8 +121,7 @@ def _parse_config_text_or_exit( experiment_config = util.safe_load_yaml_with_exceptions(config_text) if not experiment_config or not isinstance(experiment_config, dict): - print(f"Error: invalid experiment config file {path}", path) - sys.exit(1) + raise ArgumentError(None, f"Error: invalid experiment config file {path}") parse_config_overrides(experiment_config, config_overrides) @@ -279,7 +274,12 @@ def submit_experiment(args: Namespace) -> None: cli.print_warnings(resp.warnings) if not args.paused and args.follow_first_trial: - _follow_experiment_logs(sess, resp.experiment.id) + if args.publish: + port_map = proxy.parse_port_map_flag(args.publish) + with proxy.tunnel_experiment(sess, resp.experiment.id, port_map): + _follow_experiment_logs(sess, resp.experiment.id) + else: + _follow_experiment_logs(sess, resp.experiment.id) def local_experiment(args: Namespace) -> None: @@ -491,6 +491,7 @@ def get_with_offset(offset: int) -> bindings.v1GetTrialWorkloadsResponse: wl_detail = workload.checkpoint if workload.checkpoint and wl_detail: + assert isinstance(wl_detail, bindings.v1CheckpointWorkload) checkpoint_state = wl_detail.state.value checkpoint_end_time = wl_detail.endTime else: @@ -500,7 +501,7 @@ def get_with_offset(offset: int) -> bindings.v1GetTrialWorkloadsResponse: v_metrics_fields = [] if workload.validation: wl_detail = workload.validation - validation_state = wl_detail.state.value + validation_state = "STATE_COMPLETED" validation_end_time = wl_detail.endTime for name in v_metrics_names: if ( @@ -545,7 +546,7 @@ def get_with_offset(offset: int) -> bindings.v1GetTrialWorkloadsResponse: [ trial.id, wl_detail.totalBatches, - wl_detail.state.value.replace("STATE_", ""), + (checkpoint_state or validation_state).replace("STATE_", ""), render.format_time(wl_detail.endTime), ] + t_metrics_fields @@ -1071,6 +1072,14 @@ def experiment_id_arg(help: str) -> Arg: # noqa: A002 "be archived on creation.", ), ), + Arg( + "-p", + "--publish", + action="append", + default=[], + type=str, + help="publish task ports to the host", + ), ], ), # Lifecycle management commands. diff --git a/harness/determined/cli/proxy.py b/harness/determined/cli/proxy.py new file mode 100644 index 00000000000..001b5b6c5c0 --- /dev/null +++ b/harness/determined/cli/proxy.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import contextlib +import time +from typing import Iterator + +from determined.cli.tunnel import ListenerConfig, http_tunnel_listener +from determined.common.api import Session, authentication, bindings + + +@contextlib.contextmanager +def _tunnel_task(sess: Session, task_id: str, port_map: dict[int, int]) -> Iterator[None]: + # Args: + # port_map: dict of local port => task port. + # task_id: tunneled task_id. + + master_addr = sess._master + listeners = [ + ListenerConfig(service_id=f"{task_id}:{task_port}", local_port=local_port) + for local_port, task_port in port_map.items() + ] + cert = sess._cert + cert_file, cert_name = None, None + if cert is not None: + cert_file = cert.bundle + cert_name = cert.name + + token = authentication.must_cli_auth().get_session_token() + + with http_tunnel_listener(master_addr, listeners, cert_file, cert_name, token): + yield + + +@contextlib.contextmanager +def _tunnel_trial(sess: Session, trial_id: int, port_map: dict[int, int]) -> Iterator[None]: + # TODO(DET-9000): perhaps the tunnel should be able to probe master for service status, + # instead of us explicitly polling for task/trial status. + while True: + resp = bindings.get_GetTrial(sess, trialId=trial_id) + trial = resp.trial + + terminal_states = [ + bindings.experimentv1State.STATE_COMPLETED, + bindings.experimentv1State.STATE_CANCELED, + bindings.experimentv1State.STATE_ERROR, + ] + if trial.state in terminal_states: + raise ValueError("Can't tunnel a trial in terminal state") + + task_id = trial.taskId + if task_id is not None: + break + else: + time.sleep(0.1) + + with _tunnel_task(sess, task_id, port_map): + yield + + +@contextlib.contextmanager +def tunnel_experiment( + sess: Session, experiment_id: int, port_map: dict[int, int] +) -> Iterator[None]: + while True: + trials = bindings.get_GetExperimentTrials(sess, experimentId=experiment_id).trials + if len(trials) > 0: + break + else: + time.sleep(0.1) + + first_trial_id = sorted(t.id for t in trials)[0] + + with _tunnel_trial(sess, first_trial_id, port_map): + yield + + +def parse_port_map_flag(publish_arg: list[str]) -> dict[int, int]: + result = {} # type: dict[int, int] + + for e in publish_arg: + try: + if ":" in e: + lp, tp = e.split(":") + local_port, task_port = int(lp), int(tp) + result[local_port] = task_port + else: + port = int(e) + result[port] = port + except ValueError as e: + raise ValueError(f"failed to parse --publish argument: {e}") from e + + return result diff --git a/harness/determined/cli/task.py b/harness/determined/cli/task.py index 3dcbc5674d1..5f6c06c4679 100644 --- a/harness/determined/cli/task.py +++ b/harness/determined/cli/task.py @@ -34,6 +34,7 @@ def agent_info(t: Dict[str, Any]) -> Union[str, List[str]]: "Agent", "Priority", "Resource Pool", + "Ports", ] values = [ [ @@ -45,6 +46,7 @@ def agent_info(t: Dict[str, Any]) -> Union[str, List[str]]: agent_info(task), task["priority"] if task["scheduler_type"] == "priority" else "N/A", task["resource_pool"], + ",".join(map(str, sorted(pp["port"] for pp in task.get("proxy_ports", [])))), ] for task_id, task in sorted( tasks.items(), diff --git a/harness/determined/cli/tensorboard.py b/harness/determined/cli/tensorboard.py index 3eb2d4c06a4..7d588d62ae0 100644 --- a/harness/determined/cli/tensorboard.py +++ b/harness/determined/cli/tensorboard.py @@ -1,5 +1,4 @@ -import sys -from argparse import ONE_OR_MORE, FileType, Namespace +from argparse import ONE_OR_MORE, ArgumentError, FileType, Namespace from functools import partial from pathlib import Path from typing import Any, List @@ -17,8 +16,7 @@ @authentication.required def start_tensorboard(args: Namespace) -> None: if not (args.trial_ids or args.experiment_ids): - print("Either experiment_ids or trial_ids must be specified.") - sys.exit(1) + raise ArgumentError(None, "Either experiment_ids or trial_ids must be specified.") config = command.parse_config(args.config_file, None, args.config, []) diff --git a/harness/determined/cli/trial.py b/harness/determined/cli/trial.py index 9270d5bb761..cf8998addf3 100644 --- a/harness/determined/cli/trial.py +++ b/harness/determined/cli/trial.py @@ -29,18 +29,11 @@ def _format_state(state: Union[bindings.checkpointv1State, bindings.experimentv1 return str(state.value).replace("STATE_", "") -def _format_validation(validation: Optional[bindings.v1MetricsWorkload]) -> List[Any]: +def _format_validation(validation: Optional[bindings.v1MetricsWorkload]) -> Optional[str]: if not validation: - return [None, None] + return None - # TODO(ilia): Get rid of `constants` in favor of the ones from bindings. - state = _format_state(validation.state) - if state == constants.COMPLETED: - return [constants.COMPLETED, json.dumps(validation.metrics.to_json(), indent=4)] - elif state in (constants.ACTIVE, constants.ERROR): - return [state, None] - else: - raise AssertionError("Invalid state: {}".format(validation.state)) + return json.dumps(validation.metrics.to_json(), indent=4) def _format_checkpoint(checkpoint: Optional[bindings.v1CheckpointWorkload]) -> List[Any]: @@ -66,12 +59,10 @@ def _workloads_tabulate( # Print information about individual steps. headers = [ "# of Batches", - "State", "Report Time", "Checkpoint", "Checkpoint UUID", "Checkpoint Metadata", - "Validation", "Validation Metrics", ] @@ -92,10 +83,9 @@ def _workloads_tabulate( values.append( [ w_unpacked.totalBatches, - _format_state(w_unpacked.state), render.format_time(w_unpacked.endTime), *_format_checkpoint(w.checkpoint), - *_format_validation(w.validation), + _format_validation(w.validation), *row_metrics, ] ) diff --git a/harness/determined/cli/tunnel.py b/harness/determined/cli/tunnel.py index f1de7ed609c..426c7c2843c 100644 --- a/harness/determined/cli/tunnel.py +++ b/harness/determined/cli/tunnel.py @@ -4,17 +4,21 @@ """ import argparse +import contextlib import io import os import socket +import socketserver import ssl import sys import threading -from typing import Optional +import time +from dataclasses import dataclass +from typing import Iterator, List, Optional, Union import lomond -from determined.common.api import request +from determined.common.api import authentication, request class CustomSSLWebsocketSession(lomond.session.WebsocketSession): # type: ignore @@ -24,15 +28,16 @@ class CustomSSLWebsocketSession(lomond.session.WebsocketSession): # type: ignor """ def __init__( - self, socket: lomond.WebSocket, cert_file: Optional[str], cert_name: Optional[str] + self, socket: lomond.WebSocket, cert_file: Union[str, bool, None], cert_name: Optional[str] ) -> None: super().__init__(socket) self.ctx = ssl.create_default_context() - if cert_file == "False": + if cert_file == "False" or cert_file is False: self.ctx.verify_mode = ssl.CERT_NONE return if cert_file is not None: + assert isinstance(cert_file, str) self.ctx.load_verify_locations(cafile=cert_file) self.cert_name = cert_name @@ -56,11 +61,27 @@ def copy_to_websocket( ws.close() +def copy_to_websocket2( + ws: lomond.WebSocket, f: socket.socket, ready_sem: threading.Semaphore +) -> None: + ready_sem.acquire() + + try: + while True: + chunk = f.recv(4096) + if not chunk: + break + ws.send_binary(chunk) + finally: + f.close() + ws.close() + + def copy_from_websocket( f: io.RawIOBase, ws: lomond.WebSocket, ready_sem: threading.Semaphore, - cert_file: Optional[str], + cert_file: Union[str, bool, None], cert_name: Optional[str], ) -> None: try: @@ -83,13 +104,48 @@ def copy_from_websocket( f.close() +def copy_from_websocket2( + f: socket.socket, + ws: lomond.WebSocket, + ready_sem: threading.Semaphore, + cert_file: Union[str, bool, None], + cert_name: Optional[str], +) -> None: + try: + for event in ws.connect( + ping_rate=0, + session_class=lambda socket: CustomSSLWebsocketSession(socket, cert_file, cert_name), + ): + if isinstance(event, lomond.events.Binary): + f.send(event.data) + elif isinstance(event, lomond.events.Ready): + ready_sem.release() + elif isinstance( + event, + (lomond.events.ConnectFail, lomond.events.Rejected, lomond.events.ProtocolError), + ): + if isinstance(event, lomond.events.Rejected): + print(event.response) + raise Exception("Connection failed: {}".format(event)) + elif isinstance(event, (lomond.events.Closing, lomond.events.Disconnected)): + break + finally: + f.close() + + def http_connect_tunnel( - master: str, service: str, cert_file: Optional[str], cert_name: Optional[str] + master: str, + service: str, + cert_file: Union[str, bool, None], + cert_name: Optional[str], + authorization_token: Optional[str] = None, ) -> None: parsed_master = request.parse_master_address(master) assert parsed_master.hostname is not None, "Failed to parse master address: {}".format(master) url = request.make_url(master, "proxy/{}/".format(service)) ws = lomond.WebSocket(request.maybe_upgrade_ws_scheme(url)) + if authorization_token is not None: + ws.add_header(b"Authorization", f"Bearer {authorization_token}".encode()) # We can't send data to the WebSocket before the connection becomes ready, which takes a bit of # time; this semaphore lets the sending thread wait for that to happen. @@ -110,12 +166,105 @@ def http_connect_tunnel( c2.join() +@dataclass +class ListenerConfig: + service_id: str + local_port: int + local_addr: str = "0.0.0.0" + + +def _http_tunnel_listener( + master_addr: str, + tunnel: ListenerConfig, + cert_file: Union[str, bool, None], + cert_name: Optional[str], + authorization_token: Optional[str] = None, +) -> socketserver.ThreadingTCPServer: + parsed_master = request.parse_master_address(master_addr) + assert parsed_master.hostname is not None, "Failed to parse master address: {}".format( + master_addr + ) + + url = request.make_url(master_addr, "proxy/{}/".format(tunnel.service_id)) + + class TunnelHandler(socketserver.BaseRequestHandler): + def handle(self) -> None: + ws = lomond.WebSocket(request.maybe_upgrade_ws_scheme(url)) + if authorization_token is not None: + ws.add_header(b"Authorization", f"Bearer {authorization_token}".encode()) + # We can't send data to the WebSocket before the connection becomes ready, + # which takes a bit of time; this semaphore lets the sending thread + # wait for that to happen. + ready_sem = threading.Semaphore(0) + + c1 = threading.Thread(target=copy_to_websocket2, args=(ws, self.request, ready_sem)) + c2 = threading.Thread( + target=copy_from_websocket2, + args=(self.request, ws, ready_sem, cert_file, cert_name), + ) + c1.start() + c2.start() + c1.join() + c2.join() + + return socketserver.ThreadingTCPServer((tunnel.local_addr, tunnel.local_port), TunnelHandler) + + +@contextlib.contextmanager +def http_tunnel_listener( + master: str, + tunnels: List[ListenerConfig], + cert_file: Union[str, bool, None], + cert_name: Optional[str], + authorization_token: Optional[str] = None, +) -> Iterator[None]: + servers = [ + _http_tunnel_listener(master, tunnel, cert_file, cert_name, authorization_token) + for tunnel in tunnels + ] + + threads = [threading.Thread(target=lambda s: s.serve_forever(), args=(s,)) for s in servers] + + try: + for t in threads: + t.start() + # TODO(ilia): should we inform the user when we are up? + yield + finally: + for s in servers: + s.shutdown() + s.server_close() + for t in threads: + t.join() + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Tunnel through a Determined master") parser.add_argument("master_addr") parser.add_argument("service_uuid") parser.add_argument("--cert-file") parser.add_argument("--cert-name") + parser.add_argument("--listener", type=int) + parser.add_argument("-u", "--user") + parser.add_argument("--auth", action="store_true") args = parser.parse_args() - http_connect_tunnel(args.master_addr, args.service_uuid, args.cert_file, args.cert_name) + authorization_token = None + if args.auth: + auth = authentication.Authentication(args.master_addr, args.user) + authorization_token = auth.get_session_token(must=True) + + if args.listener: + with http_tunnel_listener( + args.master_addr, + [ListenerConfig(service_id=args.service_uuid, local_port=args.listener)], + args.cert_file, + args.cert_name, + authorization_token, + ): + while True: + time.sleep(1) + else: + http_connect_tunnel( + args.master_addr, args.service_uuid, args.cert_file, args.cert_name, authorization_token + ) diff --git a/harness/determined/cli/user.py b/harness/determined/cli/user.py index 4b763c961c5..dd06c4312f9 100644 --- a/harness/determined/cli/user.py +++ b/harness/determined/cli/user.py @@ -15,7 +15,16 @@ FullUser = namedtuple( "FullUser", - ["username", "admin", "active", "agent_uid", "agent_gid", "agent_user", "agent_group"], + [ + "user_id", + "username", + "admin", + "active", + "agent_uid", + "agent_gid", + "agent_user", + "agent_group", + ], ) @@ -165,7 +174,7 @@ def whoami(parsed_args: Namespace) -> None: Arg("det_username", help="name of Determined user to link"), *AGENT_USER_GROUP_ARGS, ]), - Cmd("whoami", whoami, "print the active user", []) + Cmd("whoami", whoami, "print the active user", []), ]) ] # type: List[Any] diff --git a/harness/determined/cli/workspace.py b/harness/determined/cli/workspace.py index 8d110bac5c5..ed26214f0d8 100644 --- a/harness/determined/cli/workspace.py +++ b/harness/determined/cli/workspace.py @@ -1,5 +1,5 @@ import json -from argparse import Namespace +from argparse import ArgumentError, Namespace from time import sleep from typing import Any, Dict, List, Optional, Sequence @@ -32,11 +32,9 @@ def get_workspace_id_from_args(args: Namespace) -> Optional[int]: cli.setup_session(args), args.workspace_name ) if workspace is None: - cli.report_cli_error(f'Workspace "{args.workspace_name}" not found') - return None + raise ArgumentError(None, f'Workspace "{args.workspace_name}" not found.') if workspace.archived: - cli.report_cli_error(f'Workspace "{args.workspace_name}" is archived') - return None + raise ArgumentError(None, f'Workspace "{args.workspace_name}" is archived.') workspace_id = workspace.id return workspace_id diff --git a/harness/determined/common/api/bindings.py b/harness/determined/common/api/bindings.py index 556b40430e3..89bb262c706 100644 --- a/harness/determined/common/api/bindings.py +++ b/harness/determined/common/api/bindings.py @@ -55,40 +55,6 @@ def __str__(self) -> str: return self.message -class ExpCompareTrialsSampleResponseExpTrial: - - def __init__( - self, - *, - data: "typing.Sequence[v1DataPoint]", - experimentId: int, - hparams: "typing.Dict[str, typing.Any]", - trialId: int, - ): - self.data = data - self.experimentId = experimentId - self.hparams = hparams - self.trialId = trialId - - @classmethod - def from_json(cls, obj: Json) -> "ExpCompareTrialsSampleResponseExpTrial": - kwargs: "typing.Dict[str, typing.Any]" = { - "data": [v1DataPoint.from_json(x) for x in obj["data"]], - "experimentId": obj["experimentId"], - "hparams": obj["hparams"], - "trialId": obj["trialId"], - } - return cls(**kwargs) - - def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: - out: "typing.Dict[str, typing.Any]" = { - "data": [x.to_json(omit_unset) for x in self.data], - "experimentId": self.experimentId, - "hparams": self.hparams, - "trialId": self.trialId, - } - return out - class GetHPImportanceResponseMetricHPImportance: error: "typing.Optional[str]" = None experimentProgress: "typing.Optional[float]" = None @@ -2387,70 +2353,6 @@ class v1EntityType(enum.Enum): ENTITY_TYPE_UNSPECIFIED = "ENTITY_TYPE_UNSPECIFIED" ENTITY_TYPE_PROJECT = "ENTITY_TYPE_PROJECT" -class v1ExpCompareMetricNamesResponse: - trainingMetrics: "typing.Optional[typing.Sequence[str]]" = None - validationMetrics: "typing.Optional[typing.Sequence[str]]" = None - - def __init__( - self, - *, - trainingMetrics: "typing.Union[typing.Sequence[str], None, Unset]" = _unset, - validationMetrics: "typing.Union[typing.Sequence[str], None, Unset]" = _unset, - ): - if not isinstance(trainingMetrics, Unset): - self.trainingMetrics = trainingMetrics - if not isinstance(validationMetrics, Unset): - self.validationMetrics = validationMetrics - - @classmethod - def from_json(cls, obj: Json) -> "v1ExpCompareMetricNamesResponse": - kwargs: "typing.Dict[str, typing.Any]" = { - } - if "trainingMetrics" in obj: - kwargs["trainingMetrics"] = obj["trainingMetrics"] - if "validationMetrics" in obj: - kwargs["validationMetrics"] = obj["validationMetrics"] - return cls(**kwargs) - - def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: - out: "typing.Dict[str, typing.Any]" = { - } - if not omit_unset or "trainingMetrics" in vars(self): - out["trainingMetrics"] = self.trainingMetrics - if not omit_unset or "validationMetrics" in vars(self): - out["validationMetrics"] = self.validationMetrics - return out - -class v1ExpCompareTrialsSampleResponse: - - def __init__( - self, - *, - demotedTrials: "typing.Sequence[int]", - promotedTrials: "typing.Sequence[int]", - trials: "typing.Sequence[ExpCompareTrialsSampleResponseExpTrial]", - ): - self.demotedTrials = demotedTrials - self.promotedTrials = promotedTrials - self.trials = trials - - @classmethod - def from_json(cls, obj: Json) -> "v1ExpCompareTrialsSampleResponse": - kwargs: "typing.Dict[str, typing.Any]" = { - "demotedTrials": obj["demotedTrials"], - "promotedTrials": obj["promotedTrials"], - "trials": [ExpCompareTrialsSampleResponseExpTrial.from_json(x) for x in obj["trials"]], - } - return cls(**kwargs) - - def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: - out: "typing.Dict[str, typing.Any]" = { - "demotedTrials": self.demotedTrials, - "promotedTrials": self.promotedTrials, - "trials": [x.to_json(omit_unset) for x in self.trials], - } - return out - class v1Experiment: bestTrialSearcherMetric: "typing.Optional[float]" = None checkpointCount: "typing.Optional[int]" = None @@ -6075,13 +5977,11 @@ def __init__( *, metrics: "v1Metrics", numInputs: int, - state: "experimentv1State", totalBatches: int, endTime: "typing.Union[str, None, Unset]" = _unset, ): self.metrics = metrics self.numInputs = numInputs - self.state = state self.totalBatches = totalBatches if not isinstance(endTime, Unset): self.endTime = endTime @@ -6091,7 +5991,6 @@ def from_json(cls, obj: Json) -> "v1MetricsWorkload": kwargs: "typing.Dict[str, typing.Any]" = { "metrics": v1Metrics.from_json(obj["metrics"]), "numInputs": obj["numInputs"], - "state": experimentv1State(obj["state"]), "totalBatches": obj["totalBatches"], } if "endTime" in obj: @@ -6102,7 +6001,6 @@ def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: out: "typing.Dict[str, typing.Any]" = { "metrics": self.metrics.to_json(omit_unset), "numInputs": self.numInputs, - "state": self.state.value, "totalBatches": self.totalBatches, } if not omit_unset or "endTime" in vars(self): @@ -12649,82 +12547,6 @@ def post_EnableSlot( return v1EnableSlotResponse.from_json(_resp.json()) raise APIHttpError("post_EnableSlot", _resp) -def get_ExpCompareMetricNames( - session: "api.Session", - *, - trialId: "typing.Sequence[int]", - periodSeconds: "typing.Optional[int]" = None, -) -> "typing.Iterable[v1ExpCompareMetricNamesResponse]": - _params = { - "periodSeconds": periodSeconds, - "trialId": trialId, - } - _resp = session._do_request( - method="GET", - path="/api/v1/trials/metrics-stream/metric-names", - params=_params, - json=None, - data=None, - headers=None, - timeout=None, - stream=True, - ) - if _resp.status_code == 200: - for _line in _resp.iter_lines(): - _j = json.loads(_line) - if "error" in _j: - raise APIHttpStreamError( - "get_ExpCompareMetricNames", - runtimeStreamError.from_json(_j["error"]) - ) - yield v1ExpCompareMetricNamesResponse.from_json(_j["result"]) - return - raise APIHttpError("get_ExpCompareMetricNames", _resp) - -def get_ExpCompareTrialsSample( - session: "api.Session", - *, - experimentIds: "typing.Sequence[int]", - metricName: str, - metricType: "v1MetricType", - endBatches: "typing.Optional[int]" = None, - maxDatapoints: "typing.Optional[int]" = None, - maxTrials: "typing.Optional[int]" = None, - periodSeconds: "typing.Optional[int]" = None, - startBatches: "typing.Optional[int]" = None, -) -> "typing.Iterable[v1ExpCompareTrialsSampleResponse]": - _params = { - "endBatches": endBatches, - "experimentIds": experimentIds, - "maxDatapoints": maxDatapoints, - "maxTrials": maxTrials, - "metricName": metricName, - "metricType": metricType.value, - "periodSeconds": periodSeconds, - "startBatches": startBatches, - } - _resp = session._do_request( - method="GET", - path="/api/v1/experiments-compare", - params=_params, - json=None, - data=None, - headers=None, - timeout=None, - stream=True, - ) - if _resp.status_code == 200: - for _line in _resp.iter_lines(): - _j = json.loads(_line) - if "error" in _j: - raise APIHttpStreamError( - "get_ExpCompareTrialsSample", - runtimeStreamError.from_json(_j["error"]) - ) - yield v1ExpCompareTrialsSampleResponse.from_json(_j["result"]) - return - raise APIHttpError("get_ExpCompareTrialsSample", _resp) - def get_GetActiveTasksCount( session: "api.Session", ) -> "v1GetActiveTasksCountResponse": diff --git a/harness/determined/common/api/request.py b/harness/determined/common/api/request.py index d558da8eea4..08a0bb05529 100644 --- a/harness/determined/common/api/request.py +++ b/harness/determined/common/api/request.py @@ -96,13 +96,12 @@ def do_request( if cert is None: cert = certs.cli_cert - """ - set the token and username based on this order: - - argument `auth` - - header `Authorization` - - existing cli_auth - - allocation_token - """ + # set the token and username based on this order: + # - argument `auth` + # - header `Authorization` + # - existing cli_auth + # - allocation_token + username = "" if auth is not None: if authenticated: diff --git a/harness/determined/common/schemas/expconf/_gen.py b/harness/determined/common/schemas/expconf/_gen.py index fa0da71bf41..376abcbb7ed 100644 --- a/harness/determined/common/schemas/expconf/_gen.py +++ b/harness/determined/common/schemas/expconf/_gen.py @@ -636,6 +636,14 @@ "default": [], "optionalRef": "http://determined.ai/schemas/expconf/v0/environment-variables.json" }, + "proxy_ports": { + "type": [ + "array", + "null" + ], + "default": [], + "optionalRef": "http://determined.ai/schemas/expconf/v0/proxy-ports.json" + }, "ports": { "type": [ "object", @@ -1732,6 +1740,61 @@ } } +""" + ), + "http://determined.ai/schemas/expconf/v0/proxy-port.json": json.loads( + r""" +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://determined.ai/schemas/expconf/v0/proxy-port.json", + "title": "ProxyPort", + "additionalProperties": false, + "required": [ + "proxy_port" + ], + "type": "object", + "properties": { + "proxy_port": { + "type": "number" + }, + "proxy_tcp": { + "type": [ + "boolean", + "null" + ], + "default": false + }, + "unauthenticated": { + "type": [ + "boolean", + "null" + ], + "default": false + }, + "default_service_id": { + "type": [ + "boolean", + "null" + ], + "default": false + } + } +} + +""" + ), + "http://determined.ai/schemas/expconf/v0/proxy-ports.json": json.loads( + r""" +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://determined.ai/schemas/expconf/v0/proxy-ports.json", + "title": "ProxyPortsConfig", + "type": "array", + "items": { + "$ref": "http://determined.ai/schemas/expconf/v0/proxy-port.json" + } +} + """ ), "http://determined.ai/schemas/expconf/v0/registry-auth.json": json.loads( diff --git a/harness/determined/common/schemas/expconf/_v0.py b/harness/determined/common/schemas/expconf/_v0.py index 909973dbae5..ecf4c9ac0de 100644 --- a/harness/determined/common/schemas/expconf/_v0.py +++ b/harness/determined/common/schemas/expconf/_v0.py @@ -223,12 +223,12 @@ def from_dict(cls, d: Union[dict, str], prevalidated: bool = False) -> "Environm def runtime_defaults(self) -> None: if self.cpu is None: - self.cpu = "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c" + self.cpu = "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364" if self.rocm is None: - self.rocm = "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c" + self.rocm = "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364" if self.cuda is None: - self.cuda = "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c" + self.cuda = "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364" class EnvironmentVariablesV0(schemas.SchemaBase): @@ -288,6 +288,24 @@ def to_dict(self, explicit_nones: bool = False) -> Any: return super().to_dict(explicit_nones=False) +class ProxyPortV0(schemas.SchemaBase): + _id = "http://determined.ai/schemas/expconf/v0/proxy-port.json" + proxy_port: Optional[int] = None + proxy_tcp: Optional[bool] = None + unauthenticated: Optional[bool] = None + default_service_id: Optional[bool] = None + + @schemas.auto_init + def __init__( + self, + proxy_port: Optional[int] = None, + proxy_tcp: Optional[bool] = None, + unauthenticated: Optional[bool] = None, + default_service_id: Optional[bool] = None, + ) -> None: + pass + + class EnvironmentConfigV0(schemas.SchemaBase): _id = "http://determined.ai/schemas/expconf/v0/environment.json" add_capabilities: Optional[List[str]] = None @@ -298,6 +316,7 @@ class EnvironmentConfigV0(schemas.SchemaBase): pod_spec: Optional[Dict[str, Any]] = None ports: Optional[Dict[str, int]] = None registry_auth: Optional[RegistryAuthConfigV0] = None + proxy_ports: Optional[List[ProxyPortV0]] = None @schemas.auto_init def __init__( @@ -310,6 +329,7 @@ def __init__( pod_spec: Optional[Dict[str, Any]] = None, ports: Optional[Dict[str, int]] = None, registry_auth: Optional[RegistryAuthConfigV0] = None, + proxy_ports: Optional[List[ProxyPortV0]] = None, ) -> None: pass diff --git a/harness/determined/core/_distributed.py b/harness/determined/core/_distributed.py index 5cb6890c20d..e49ddceed5b 100644 --- a/harness/determined/core/_distributed.py +++ b/harness/determined/core/_distributed.py @@ -111,10 +111,6 @@ def _init_ipc(self, force_tcp: bool) -> None: ) self._worker_zmq.safe_start() - if self.local_size < 2: - # No local broadcasting necessary. - return - # Local broadcast server. self.tempdir = None if self.local_size < 2: diff --git a/harness/determined/deploy/aws/cli.py b/harness/determined/deploy/aws/cli.py index 7a5db180231..608172b441d 100644 --- a/harness/determined/deploy/aws/cli.py +++ b/harness/determined/deploy/aws/cli.py @@ -2,7 +2,6 @@ import base64 import json import re -import sys from pathlib import Path from typing import Callable, Dict, Tuple, Type @@ -10,6 +9,7 @@ from botocore.exceptions import NoCredentialsError from termcolor import colored +from determined.cli.errors import CliError from determined.common.declarative_argparse import Arg, ArgGroup, BoolOptArg, Cmd from determined.deploy.errors import MasterTimeoutExpired @@ -55,11 +55,10 @@ def error_no_credentials() -> None: colored("Unable to locate AWS credentials.", "red"), "Did you run %s?" % colored("aws configure", "yellow"), ) - print( - "See the AWS Documentation for information on how to use AWS credentials:", + raise CliError( + "See the AWS Documentation for information on how to use AWS credentials: " "https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html", ) - sys.exit(1) def get_deployment_class(deployment_type: str) -> Type[base.DeterminedDeployment]: @@ -84,8 +83,7 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: f"`det deploy` is only supported in {constants.misc.SUPPORTED_REGIONS} - " f"tried to deploy to {boto3_session.region_name}" ) - print("use the --region argument to deploy to a supported region") - sys.exit(1) + raise CliError("use the --region argument to deploy to a supported region") if command == "list": try: @@ -93,9 +91,10 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: except NoCredentialsError: error_no_credentials() except Exception as e: - print(e) - print("Listing stacks failed. Check the AWS CloudFormation Console for details.") - sys.exit(1) + raise CliError( + "Listing stacks failed. Check the AWS CloudFormation Console for details.", + e_stack=e, + ) for item in output: print(item["StackName"]) return @@ -109,8 +108,7 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: # sys.exit(1) if not re.match(constants.misc.CLOUDFORMATION_REGEX, args.cluster_id): - print("Deployment Failed - cluster-id much match ^[a-zA-Z][-a-zA-Z0-9]*$") - sys.exit(1) + raise CliError("Deployment Failed - cluster-id much match ^[a-zA-Z][-a-zA-Z0-9]*$") if command == "down": if not args.yes: @@ -128,9 +126,10 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: except NoCredentialsError: error_no_credentials() except Exception as e: - print(e) - print("Stack Deletion Failed. Check the AWS CloudFormation Console for details.") - sys.exit(1) + raise CliError( + "Stack Deletion Failed. Check the AWS CloudFormation Console for details.", + e_stack=e, + ) print("Delete Successful") return @@ -138,8 +137,7 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: if (args.cpu_env_image and not args.gpu_env_image) or ( args.gpu_env_image and not args.cpu_env_image ): - print("If a CPU or GPU environment image is specified, both should be.") - sys.exit(1) + raise CliError("If a CPU or GPU environment image is specified, both should be.") if args.deployment_type != constants.deployment_types.SIMPLE: if args.agent_subnet_id is not None: @@ -247,13 +245,9 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: except NoCredentialsError: error_no_credentials() except Exception as e: - print(e) - print( - colored( - "Stack Deployment Failed. Check the AWS CloudFormation Console for details.", "red" - ) + raise CliError( + "Stack Deployment Failed. Check the AWS CloudFormation Console for details.", e_stack=e ) - sys.exit(1) if not args.no_wait_for_master: try: @@ -265,8 +259,9 @@ def deploy_aws(command: str, args: argparse.Namespace) -> None: "red", ) ) - print("For details, SSH to master instance and check /var/log/cloud-init-output.log.") - sys.exit(1) + raise CliError( + "For details, SSH to master instance and check /var/log/cloud-init-output.log." + ) print("Determined Deployment Successful") diff --git a/harness/determined/deploy/aws/templates/efs.yaml b/harness/determined/deploy/aws/templates/efs.yaml index f9d0342f2c5..afec737d43c 100644 --- a/harness/determined/deploy/aws/templates/efs.yaml +++ b/harness/determined/deploy/aws/templates/efs.yaml @@ -3,35 +3,35 @@ Mappings: RegionMap: ap-northeast-1: Master: ami-0c7cb70d3eb61492b - Agent: ami-0efbc837b3c729df1 + Agent: ami-09c9ea05363b4fe2d # TODO(DET-4258) Uncomment these when we fully support all P3 regions. # ap-northeast-2: # Master: ami-003bb1772f36a39a3 - # Agent: ami-0934d35fc17d76abc + # Agent: ami-07b13ec47230370d5 # ap-southeast-1: # Master: ami-09f03fa5572692399 - # Agent: ami-05068dcfa829e229e + # Agent: ami-07affc5a669acb69f # ap-southeast-2: # Master: ami-06139e5e22cc2f7b1 - # Agent: ami-0e62ea531c3969a65 + # Agent: ami-0ef38d26d9f61a0db eu-central-1: Master: ami-0b81e95bb0a06ea8c - Agent: ami-051a91b2829200200 + Agent: ami-0c09eb6495c53c53c eu-west-1: Master: ami-029cfca952b331b52 - Agent: ami-04eab4dc55258e621 + Agent: ami-06de67607d7b543dd # eu-west-2: # Master: ami-035469b606478d63d - # Agent: ami-08aa94155b641d19d + # Agent: ami-0130ec158c7260bf1 us-east-1: Master: ami-0b93ce03dcbcb10f6 - Agent: ami-0b4cc2ed4ecd07a2d + Agent: ami-027145fb15545cd96 us-east-2: Master: ami-0cbea92f2377277a4 - Agent: ami-0ba898a163ad80bf9 + Agent: ami-0fe46b7b5fdba7b51 us-west-2: Master: ami-0d31d7c9fc9503726 - Agent: ami-0a483013f2e53ed60 + Agent: ami-03bad0b25097fe67d Parameters: VpcCIDR: @@ -101,7 +101,7 @@ Parameters: Version: Type: String Description: Determined version or commit for master image - Default: 0.19.12-dev0 + Default: 0.20.1-dev0 DBPassword: Type: String diff --git a/harness/determined/deploy/aws/templates/fsx.yaml b/harness/determined/deploy/aws/templates/fsx.yaml index 02237802ddb..95c2f888d40 100644 --- a/harness/determined/deploy/aws/templates/fsx.yaml +++ b/harness/determined/deploy/aws/templates/fsx.yaml @@ -3,35 +3,35 @@ Mappings: RegionMap: ap-northeast-1: Master: ami-0c7cb70d3eb61492b - Agent: ami-0efbc837b3c729df1 + Agent: ami-09c9ea05363b4fe2d # TODO(DET-4258) Uncomment these when we fully support all P3 regions. # ap-northeast-2: # Master: ami-003bb1772f36a39a3 - # Agent: ami-0934d35fc17d76abc + # Agent: ami-07b13ec47230370d5 # ap-southeast-1: # Master: ami-09f03fa5572692399 - # Agent: ami-05068dcfa829e229e + # Agent: ami-07affc5a669acb69f # ap-southeast-2: # Master: ami-06139e5e22cc2f7b1 - # Agent: ami-0e62ea531c3969a65 + # Agent: ami-0ef38d26d9f61a0db eu-central-1: Master: ami-0b81e95bb0a06ea8c - Agent: ami-051a91b2829200200 + Agent: ami-0c09eb6495c53c53c eu-west-1: Master: ami-029cfca952b331b52 - Agent: ami-04eab4dc55258e621 + Agent: ami-06de67607d7b543dd # eu-west-2: # Master: ami-035469b606478d63d - # Agent: ami-08aa94155b641d19d + # Agent: ami-0130ec158c7260bf1 us-east-1: Master: ami-0b93ce03dcbcb10f6 - Agent: ami-0b4cc2ed4ecd07a2d + Agent: ami-027145fb15545cd96 us-east-2: Master: ami-0cbea92f2377277a4 - Agent: ami-0ba898a163ad80bf9 + Agent: ami-0fe46b7b5fdba7b51 us-west-2: Master: ami-0d31d7c9fc9503726 - Agent: ami-0a483013f2e53ed60 + Agent: ami-03bad0b25097fe67d Parameters: VpcCIDR: @@ -101,7 +101,7 @@ Parameters: Version: Type: String Description: Determined version or commit for master image - Default: 0.19.12-dev0 + Default: 0.20.1-dev0 DBPassword: Type: String diff --git a/harness/determined/deploy/aws/templates/govcloud.yaml b/harness/determined/deploy/aws/templates/govcloud.yaml index 9f42e3c72d8..43cb2529b02 100644 --- a/harness/determined/deploy/aws/templates/govcloud.yaml +++ b/harness/determined/deploy/aws/templates/govcloud.yaml @@ -67,7 +67,7 @@ Parameters: Version: Type: String Description: Determined version or commit for master docker image - Default: 0.19.12-dev0 + Default: 0.20.1-dev0 DBPassword: Type: String diff --git a/harness/determined/deploy/aws/templates/secure.yaml b/harness/determined/deploy/aws/templates/secure.yaml index 21215845415..9b71f0240fe 100644 --- a/harness/determined/deploy/aws/templates/secure.yaml +++ b/harness/determined/deploy/aws/templates/secure.yaml @@ -4,44 +4,44 @@ Mappings: RegionMap: ap-northeast-1: Master: ami-0c7cb70d3eb61492b - Agent: ami-0efbc837b3c729df1 + Agent: ami-09c9ea05363b4fe2d Bastion: ami-0c7cb70d3eb61492b # TODO(DET-4258) Uncomment these when we fully support all P3 regions. # ap-northeast-2: # Master: ami-003bb1772f36a39a3 - # Agent: ami-0934d35fc17d76abc + # Agent: ami-07b13ec47230370d5 # Bastion: ami-003bb1772f36a39a3 # ap-southeast-1: # Master: ami-09f03fa5572692399 - # Agent: ami-05068dcfa829e229e + # Agent: ami-07affc5a669acb69f # Bastion: ami-09f03fa5572692399 # ap-southeast-2: # Master: ami-06139e5e22cc2f7b1 - # Agent: ami-0e62ea531c3969a65 + # Agent: ami-0ef38d26d9f61a0db # Bastion: ami-06139e5e22cc2f7b1 eu-central-1: Master: ami-0b81e95bb0a06ea8c - Agent: ami-051a91b2829200200 + Agent: ami-0c09eb6495c53c53c Bastion: ami-0b81e95bb0a06ea8c eu-west-1: Master: ami-029cfca952b331b52 - Agent: ami-04eab4dc55258e621 + Agent: ami-06de67607d7b543dd Bastion: ami-029cfca952b331b52 # eu-west-2: # Master: ami-035469b606478d63d - # Agent: ami-08aa94155b641d19d + # Agent: ami-0130ec158c7260bf1 # Bastion: ami-035469b606478d63d us-east-1: Master: ami-0b93ce03dcbcb10f6 - Agent: ami-0b4cc2ed4ecd07a2d + Agent: ami-027145fb15545cd96 Bastion: ami-0b93ce03dcbcb10f6 us-east-2: Master: ami-0cbea92f2377277a4 - Agent: ami-0ba898a163ad80bf9 + Agent: ami-0fe46b7b5fdba7b51 Bastion: ami-0cbea92f2377277a4 us-west-2: Master: ami-0d31d7c9fc9503726 - Agent: ami-0a483013f2e53ed60 + Agent: ami-03bad0b25097fe67d Bastion: ami-0d31d7c9fc9503726 Parameters: @@ -122,7 +122,7 @@ Parameters: Version: Type: String Description: Determined version or commit for master image - Default: 0.19.12-dev0 + Default: 0.20.1-dev0 DBPassword: Type: String diff --git a/harness/determined/deploy/aws/templates/simple.yaml b/harness/determined/deploy/aws/templates/simple.yaml index 44df81c7afe..d8b172577bd 100644 --- a/harness/determined/deploy/aws/templates/simple.yaml +++ b/harness/determined/deploy/aws/templates/simple.yaml @@ -5,35 +5,35 @@ Mappings: RegionMap: ap-northeast-1: Master: ami-0c7cb70d3eb61492b - Agent: ami-0efbc837b3c729df1 + Agent: ami-09c9ea05363b4fe2d # TODO(DET-4258) Uncomment these when we fully support all P3 regions. # ap-northeast-2: # Master: ami-003bb1772f36a39a3 - # Agent: ami-0934d35fc17d76abc + # Agent: ami-07b13ec47230370d5 # ap-southeast-1: # Master: ami-09f03fa5572692399 - # Agent: ami-05068dcfa829e229e + # Agent: ami-07affc5a669acb69f # ap-southeast-2: # Master: ami-06139e5e22cc2f7b1 - # Agent: ami-0e62ea531c3969a65 + # Agent: ami-0ef38d26d9f61a0db eu-central-1: Master: ami-0b81e95bb0a06ea8c - Agent: ami-051a91b2829200200 + Agent: ami-0c09eb6495c53c53c eu-west-1: Master: ami-029cfca952b331b52 - Agent: ami-04eab4dc55258e621 + Agent: ami-06de67607d7b543dd # eu-west-2: # Master: ami-035469b606478d63d - # Agent: ami-08aa94155b641d19d + # Agent: ami-0130ec158c7260bf1 us-east-1: Master: ami-0b93ce03dcbcb10f6 - Agent: ami-0b4cc2ed4ecd07a2d + Agent: ami-027145fb15545cd96 us-east-2: Master: ami-0cbea92f2377277a4 - Agent: ami-0ba898a163ad80bf9 + Agent: ami-0fe46b7b5fdba7b51 us-west-2: Master: ami-0d31d7c9fc9503726 - Agent: ami-0a483013f2e53ed60 + Agent: ami-03bad0b25097fe67d Parameters: Keypair: @@ -93,7 +93,7 @@ Parameters: Version: Type: String Description: Determined version or commit for master docker image - Default: 0.19.12-dev0 + Default: 0.20.1-dev0 DBPassword: Type: String diff --git a/harness/determined/deploy/gcp/cli.py b/harness/determined/deploy/gcp/cli.py index 38ec21dc501..c7127f1b27a 100644 --- a/harness/determined/deploy/gcp/cli.py +++ b/harness/determined/deploy/gcp/cli.py @@ -2,12 +2,11 @@ import os import sys from pathlib import Path -from typing import Callable +from typing import Callable, Tuple import pkg_resources from termcolor import colored -import determined import determined.deploy from determined.common.declarative_argparse import Arg, ArgGroup, Cmd from determined.deploy.errors import MasterTimeoutExpired @@ -61,6 +60,8 @@ def deploy_gcp(command: str, args: argparse.Namespace) -> None: print("Delete Successful") return + det_configs["labels"] = dict(det_configs.get("add_label", [])) + # Handle Up subcommand. if (args.cpu_env_image and not args.gpu_env_image) or ( args.gpu_env_image and not args.cpu_env_image @@ -93,6 +94,7 @@ def deploy_gcp(command: str, args: argparse.Namespace) -> None: "master_config_template_path", "tf_state_gcs_bucket_name", "func", + "add_label", "_command", "_subcommand", "_subsubcommand", @@ -144,6 +146,22 @@ def handle_dump_master_config_template(args: argparse.Namespace) -> None: print(fin.read()) +def parse_add_label() -> Callable: + def parse(s: str) -> Tuple[str, str]: + try: + key, value = s.split("=", 1) + except ValueError: + raise argparse.ArgumentTypeError("key=value format requires both a key and a value") + + if not key or not value: + raise argparse.ArgumentTypeError( + "both key and value must be defined in key=value format" + ) + return key, value + + return parse + + args_description = Cmd( "gcp", None, @@ -417,6 +435,14 @@ def handle_dump_master_config_template(args: argparse.Namespace) -> None: help="use the GCS bucket to store the terraform state " "instead of a local directory", ), + Arg( + "--add-label", + type=parse_add_label(), + action="append", + default=None, + help="apply label to master instance in key=value format, " + "can be repeated", + ), ], ), ], diff --git a/harness/determined/deploy/gcp/constants.py b/harness/determined/deploy/gcp/constants.py index 1c5d0d281e2..81127727a3d 100644 --- a/harness/determined/deploy/gcp/constants.py +++ b/harness/determined/deploy/gcp/constants.py @@ -3,7 +3,7 @@ class defaults: AUX_AGENT_INSTANCE_TYPE = "n1-standard-4" COMPUTE_AGENT_INSTANCE_TYPE = "n1-standard-32" DB_PASSWORD = "postgres" - ENVIRONMENT_IMAGE = "det-environments-ad0591c" + ENVIRONMENT_IMAGE = "det-environments-7aa5364" GPU_NUM = 8 GPU_TYPE = "nvidia-tesla-k80" MASTER_INSTANCE_TYPE = "n1-standard-2" diff --git a/harness/determined/deploy/gcp/terraform/main.tf b/harness/determined/deploy/gcp/terraform/main.tf index 843d0ca4cc5..0c1c94b08ad 100644 --- a/harness/determined/deploy/gcp/terraform/main.tf +++ b/harness/determined/deploy/gcp/terraform/main.tf @@ -35,6 +35,7 @@ module "network" { project_id = var.project_id unique_id = local.unique_id network = var.network + labels = var.labels } @@ -73,6 +74,7 @@ module "filestore" { source = "./modules/filestore" project_id = var.project_id + labels = var.labels zone = var.zone network_name = module.network.network_name @@ -99,6 +101,7 @@ module "gcs" { unique_id = local.unique_id gcs_bucket = var.gcs_bucket + labels = var.labels service_account_email = module.service_account.service_account_email } @@ -115,6 +118,7 @@ module "database" { db_username = var.db_username db_password = var.db_password db_version = var.db_version + user_labels = var.labels network_self_link = module.network.network_self_link service_networking_connection = module.network.service_networking_connection } @@ -176,6 +180,7 @@ module "compute" { preemption_enabled = var.preemption_enabled cpu_env_image = var.cpu_env_image gpu_env_image = var.gpu_env_image + labels = var.labels network_name = module.network.network_name subnetwork_name = module.network.subnetwork_name diff --git a/harness/determined/deploy/gcp/terraform/modules/compute/main.tf b/harness/determined/deploy/gcp/terraform/modules/compute/main.tf index 924e36b78df..9ac8e176982 100644 --- a/harness/determined/deploy/gcp/terraform/modules/compute/main.tf +++ b/harness/determined/deploy/gcp/terraform/modules/compute/main.tf @@ -9,6 +9,7 @@ resource "google_compute_instance" "master_instance" { machine_type = var.master_instance_type zone = var.zone tags = [var.tag_master_port, var.tag_allow_internal, var.tag_allow_ssh] + labels = var.labels boot_disk { initialize_params { diff --git a/harness/determined/deploy/gcp/terraform/modules/compute/variables.tf b/harness/determined/deploy/gcp/terraform/modules/compute/variables.tf index ab4985f4323..ee7ae98363a 100644 --- a/harness/determined/deploy/gcp/terraform/modules/compute/variables.tf +++ b/harness/determined/deploy/gcp/terraform/modules/compute/variables.tf @@ -138,3 +138,6 @@ variable "gpu_env_image" { variable "master_config_template" { } + +variable "labels" { +} diff --git a/harness/determined/deploy/gcp/terraform/modules/database/main.tf b/harness/determined/deploy/gcp/terraform/modules/database/main.tf index 54c5fa3b13e..b848f2b72c0 100644 --- a/harness/determined/deploy/gcp/terraform/modules/database/main.tf +++ b/harness/determined/deploy/gcp/terraform/modules/database/main.tf @@ -21,6 +21,7 @@ resource "google_sql_database_instance" "db_instance" { settings { tier = var.db_tier + user_labels = var.user_labels ip_configuration { ipv4_enabled = false private_network = var.network_self_link diff --git a/harness/determined/deploy/gcp/terraform/modules/database/variables.tf b/harness/determined/deploy/gcp/terraform/modules/database/variables.tf index 61ca58f1554..0818af124d5 100644 --- a/harness/determined/deploy/gcp/terraform/modules/database/variables.tf +++ b/harness/determined/deploy/gcp/terraform/modules/database/variables.tf @@ -23,3 +23,6 @@ variable "unique_id" { variable "service_networking_connection" { } + +variable "user_labels" { +} diff --git a/harness/determined/deploy/gcp/terraform/modules/filestore/main.tf b/harness/determined/deploy/gcp/terraform/modules/filestore/main.tf index 719d39040de..0a3f18b901e 100644 --- a/harness/determined/deploy/gcp/terraform/modules/filestore/main.tf +++ b/harness/determined/deploy/gcp/terraform/modules/filestore/main.tf @@ -10,6 +10,7 @@ resource "random_integer" "naming_int" { resource "google_filestore_instance" "persistence-filestore" { name = "det-filestore-${var.unique_id}-${random_integer.naming_int.result}" + labels = var.labels zone = var.zone tier = "BASIC_HDD" file_shares { diff --git a/harness/determined/deploy/gcp/terraform/modules/filestore/variables.tf b/harness/determined/deploy/gcp/terraform/modules/filestore/variables.tf index 6ebdd00e939..cecff1c930f 100644 --- a/harness/determined/deploy/gcp/terraform/modules/filestore/variables.tf +++ b/harness/determined/deploy/gcp/terraform/modules/filestore/variables.tf @@ -9,3 +9,6 @@ variable "network_name" { variable "unique_id" { } + +variable "labels" { +} diff --git a/harness/determined/deploy/gcp/terraform/modules/gcs/main.tf b/harness/determined/deploy/gcp/terraform/modules/gcs/main.tf index 103843569a9..0e8f7e4df0f 100644 --- a/harness/determined/deploy/gcp/terraform/modules/gcs/main.tf +++ b/harness/determined/deploy/gcp/terraform/modules/gcs/main.tf @@ -12,6 +12,7 @@ resource "google_storage_bucket" "checkpoint_store" { name = "det-checkpoints-${var.unique_id}-${random_integer.naming_int.result}" force_destroy = true + labels = var.labels } resource "google_storage_bucket_iam_binding" "checkpoint_editor" { diff --git a/harness/determined/deploy/gcp/terraform/modules/gcs/variables.tf b/harness/determined/deploy/gcp/terraform/modules/gcs/variables.tf index 309f8792df6..cf4271be347 100644 --- a/harness/determined/deploy/gcp/terraform/modules/gcs/variables.tf +++ b/harness/determined/deploy/gcp/terraform/modules/gcs/variables.tf @@ -9,3 +9,6 @@ variable "service_account_email" { variable "gcs_bucket" { type = string } + +variable "labels" { +} diff --git a/harness/determined/deploy/gcp/terraform/modules/network/main.tf b/harness/determined/deploy/gcp/terraform/modules/network/main.tf index b9729fa4bd9..6a605c3adab 100644 --- a/harness/determined/deploy/gcp/terraform/modules/network/main.tf +++ b/harness/determined/deploy/gcp/terraform/modules/network/main.tf @@ -20,6 +20,7 @@ resource "google_compute_global_address" "private_ip_address" { address_type = "INTERNAL" prefix_length = 16 network = local.network_self_link + labels = var.labels } resource "google_service_networking_connection" "private_vpc_connection" { diff --git a/harness/determined/deploy/gcp/terraform/modules/network/variables.tf b/harness/determined/deploy/gcp/terraform/modules/network/variables.tf index 87fd4d24f3d..59d9b078f63 100644 --- a/harness/determined/deploy/gcp/terraform/modules/network/variables.tf +++ b/harness/determined/deploy/gcp/terraform/modules/network/variables.tf @@ -15,3 +15,5 @@ variable "subnetwork" { default = null } +variable "labels" { +} diff --git a/harness/determined/deploy/gcp/terraform/variables.tf b/harness/determined/deploy/gcp/terraform/variables.tf index 9ce276b3034..a48ec6b8aff 100644 --- a/harness/determined/deploy/gcp/terraform/variables.tf +++ b/harness/determined/deploy/gcp/terraform/variables.tf @@ -163,6 +163,11 @@ variable "preemption_enabled" { default = false } +variable "labels" { + type = map + default = {} +} + /****************************************** Determined *****************************************/ diff --git a/harness/determined/estimator/_estimator_trial.py b/harness/determined/estimator/_estimator_trial.py index de7a77c02fc..14412d24e7b 100644 --- a/harness/determined/estimator/_estimator_trial.py +++ b/harness/determined/estimator/_estimator_trial.py @@ -819,7 +819,7 @@ class EstimatorTrial(det.Trial): """ By default, experiments run with TensorFlow 1.x. To configure your trial to use TensorFlow 2.x, set a TF 2.x image in the experiment configuration - (e.g. ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12``). + (e.g. ``determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1``). ``EstimatorTrial`` supports TF 2.x; however it uses TensorFlow V1 behavior. We have disabled TensorFlow V2 behavior for ``EstimatorTrial``, diff --git a/harness/determined/keras/_tf_keras_trial.py b/harness/determined/keras/_tf_keras_trial.py index 2559e919358..d08f4ca0482 100644 --- a/harness/determined/keras/_tf_keras_trial.py +++ b/harness/determined/keras/_tf_keras_trial.py @@ -974,7 +974,7 @@ class TFKerasTrial(det.Trial): legacy TensorFlow 1.x, specify a TensorFlow 1.x image in the :ref:`environment.image ` field of the experiment configuration (e.g., - ``determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.12``). + ``determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.20.1``). Trials default to using eager execution with TensorFlow 2.x but not with TensorFlow 1.x. To override the default behavior, call the appropriate diff --git a/harness/determined/tensorboard/util.py b/harness/determined/tensorboard/util.py index 9140954747f..babf63ef050 100644 --- a/harness/determined/tensorboard/util.py +++ b/harness/determined/tensorboard/util.py @@ -1,6 +1,7 @@ import logging import pathlib -from typing import List +import re +from typing import List, Optional tb_file_types = [ "*tfevents*", @@ -17,11 +18,23 @@ ".xplane.pb", ".kernel_stats.pb", ".overview_page.pb", - ".pt.trace.json", ".trace.json.gz", ".trace.json", ] +pytorch_profiler_file_extensions = [ + ".pt.trace.json", + ".pt.trace.json.gz", +] + +pytorch_profiler_file_pattern = re.compile( + r"""^(.*?) # worker name + (\.\d+)? # optional timestamp like 1619499959628 used as span name + \.pt\.trace\.json # the ending suffix + (?:\.gz)?$""", + re.X, +) # optional .gz extension + def find_tb_files(base_dir: pathlib.Path) -> List[pathlib.Path]: """ @@ -47,6 +60,11 @@ def get_rank_aware_path(path: pathlib.Path, rank: int) -> pathlib.Path: For example, with rank = 3 "2022_05_13_15_25_41/ip-172-31-8-212.input_pipeline.pb" will become "2022_05_13_15_25_41/ip-172-31-8-212#3.input_pipeline.pb" """ + + pytorch_profiler_extension = get_pytorch_profiler_file_extension(path) + if pytorch_profiler_extension: + return _get_rank_aware_path_pytorch_profiler(path, pytorch_profiler_extension, rank) + for ext in profiler_file_extensions: if path.match(f"*{ext}"): num_parts = ext.count(".") @@ -56,3 +74,49 @@ def get_rank_aware_path(path: pathlib.Path, rank: int) -> pathlib.Path: path = path.with_name(f"{path.name}#{rank}{ext}") return path return path + + +def _get_rank_aware_path_pytorch_profiler( + path: pathlib.Path, pytorch_profiler_extension: str, rank: int +) -> pathlib.Path: + path_parts = path.parts + file_name = path_parts[-1] + match = pytorch_profiler_file_pattern.match(file_name) + + if match: + match_groups = match.groups() + + worker = match_groups[0] + worker = f"{worker}#{rank}" + + span = match_groups[1] + + if span: + file_name = f"{worker}{span}{pytorch_profiler_extension}" + else: + file_name = f"{worker}{pytorch_profiler_extension}" + + if len(path_parts) == 1: + # only file name is passed in + return pathlib.Path(file_name) + else: + # path with directory is passed in + output_path = pathlib.Path(path_parts[0]) + for i in range(1, len(path_parts) - 1): + output_path = output_path.joinpath(path_parts[i]) + output_path = output_path.joinpath(file_name) + return output_path + else: + raise Exception( + ( + f"Path: {path} has pytorch profiler extension {pytorch_profiler_extension}", + "but no matching file pattern", + ) + ) + + +def get_pytorch_profiler_file_extension(path: pathlib.Path) -> Optional[str]: + for ext in pytorch_profiler_file_extensions: + if path.match(f"*{ext}"): + return ext + return None diff --git a/harness/mypy.ini b/harness/mypy.ini index 1a77a667453..eaad1199210 100644 --- a/harness/mypy.ini +++ b/harness/mypy.ini @@ -16,7 +16,7 @@ warn_redundant_casts = True warn_return_any = True warn_unused_configs = True warn_unused_ignores = True -exclude = (build|tests/experiment/fixtures/ancient.checkpoints/.*) +exclude = (build|tests/fixtures/import_from_path/.*|tests/experiment/fixtures/ancient.checkpoints/.*) [mypy-torch.nn.*] ; site-packages/torch/nn/quantized/functional.py:219: error: Type signature has too few arguments diff --git a/harness/setup.py b/harness/setup.py index b96f18072cf..3edf9b451e5 100644 --- a/harness/setup.py +++ b/harness/setup.py @@ -2,7 +2,7 @@ setup( name="determined", - version="0.19.12-dev0", + version="0.20.1-dev0", author="Determined AI", author_email="hello@determined.ai", url="https://determined.ai/", diff --git a/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-estimator/metadata.json b/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-estimator/metadata.json index cc85c1324cc..66084195604 100644 --- a/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-estimator/metadata.json +++ b/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-estimator/metadata.json @@ -39,9 +39,9 @@ }, "force_pull_image": false, "image": { - "cpu": "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c", - "cuda": "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c", - "rocm": "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c" + "cpu": "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364", + "cuda": "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364", + "rocm": "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364" }, "pod_spec": null, "ports": {}, diff --git a/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-keras/metadata.json b/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-keras/metadata.json index 2f76eebc7a7..7dae3109701 100644 --- a/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-keras/metadata.json +++ b/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-keras/metadata.json @@ -39,9 +39,9 @@ }, "force_pull_image": false, "image": { - "cpu": "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c", - "cuda": "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c", - "rocm": "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c" + "cpu": "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364", + "cuda": "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364", + "rocm": "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364" }, "pod_spec": null, "ports": {}, diff --git a/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-pytorch/metadata.json b/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-pytorch/metadata.json index 1195ee54a07..96af52f4db7 100644 --- a/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-pytorch/metadata.json +++ b/harness/tests/experiment/fixtures/ancient-checkpoints/0.17.6-pytorch/metadata.json @@ -38,9 +38,9 @@ }, "force_pull_image": false, "image": { - "cpu": "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c", - "cuda": "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c", - "rocm": "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c" + "cpu": "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364", + "cuda": "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364", + "rocm": "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364" }, "pod_spec": null, "ports": {}, diff --git a/harness/tests/fixtures/import_from_path/a/data.py b/harness/tests/fixtures/import_from_path/a/data.py new file mode 100644 index 00000000000..6f6136d18da --- /dev/null +++ b/harness/tests/fixtures/import_from_path/a/data.py @@ -0,0 +1 @@ +val = "a" diff --git a/harness/tests/fixtures/import_from_path/a/model_def.py b/harness/tests/fixtures/import_from_path/a/model_def.py new file mode 100644 index 00000000000..a51cf690bfd --- /dev/null +++ b/harness/tests/fixtures/import_from_path/a/model_def.py @@ -0,0 +1,8 @@ +import data +import lib1 +import lib2 + +val = "a" +data_val = data.val +lib1_id = id(lib1) +lib2_id = id(lib2) diff --git a/harness/tests/fixtures/import_from_path/b/data.py b/harness/tests/fixtures/import_from_path/b/data.py new file mode 100644 index 00000000000..6c84570f05c --- /dev/null +++ b/harness/tests/fixtures/import_from_path/b/data.py @@ -0,0 +1 @@ +val = "b" diff --git a/harness/tests/fixtures/import_from_path/b/model_def.py b/harness/tests/fixtures/import_from_path/b/model_def.py new file mode 100644 index 00000000000..373ce7cca4d --- /dev/null +++ b/harness/tests/fixtures/import_from_path/b/model_def.py @@ -0,0 +1,8 @@ +import data +import lib1 +import lib2 + +val = "b" +data_val = data.val +lib1_id = id(lib1) +lib2_id = id(lib2) diff --git a/harness/tests/fixtures/import_from_path/c/data.py b/harness/tests/fixtures/import_from_path/c/data.py new file mode 100644 index 00000000000..745a6b43fca --- /dev/null +++ b/harness/tests/fixtures/import_from_path/c/data.py @@ -0,0 +1 @@ +val = "c" diff --git a/harness/tests/fixtures/import_from_path/c/model_def.py b/harness/tests/fixtures/import_from_path/c/model_def.py new file mode 100644 index 00000000000..434c59a4899 --- /dev/null +++ b/harness/tests/fixtures/import_from_path/c/model_def.py @@ -0,0 +1,8 @@ +import data +import lib1 +import lib2 + +val = "c" +data_val = data.val +lib1_id = id(lib1) +lib2_id = id(lib2) diff --git a/harness/tests/fixtures/import_from_path/libraries/lib1.py b/harness/tests/fixtures/import_from_path/libraries/lib1.py new file mode 100644 index 00000000000..71ba39f71e6 --- /dev/null +++ b/harness/tests/fixtures/import_from_path/libraries/lib1.py @@ -0,0 +1 @@ +# this module has been intentionally left blank diff --git a/harness/tests/fixtures/import_from_path/libraries/lib2.py b/harness/tests/fixtures/import_from_path/libraries/lib2.py new file mode 100644 index 00000000000..71ba39f71e6 --- /dev/null +++ b/harness/tests/fixtures/import_from_path/libraries/lib2.py @@ -0,0 +1 @@ +# this module has been intentionally left blank diff --git a/harness/tests/tensorboard/test_util.py b/harness/tests/tensorboard/test_util.py index ba3570aba69..650f89b7d06 100644 --- a/harness/tests/tensorboard/test_util.py +++ b/harness/tests/tensorboard/test_util.py @@ -100,6 +100,7 @@ def test_list_tb_files_nonexistent_directory(tmp_path: pathlib.Path) -> None: 2, "/home/bob/tensorboard/the-host-name.some-extension.gz", ), + # Pytorch profiler file with timestamp and ends with pt.trace.json ( ( "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" @@ -108,9 +109,57 @@ def test_list_tb_files_nonexistent_directory(tmp_path: pathlib.Path) -> None: 1, ( "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" - "aa1f87508336_37.1674696139174#1.pt.trace.json" + "aa1f87508336_37#1.1674696139174.pt.trace.json" ), ), + # Pytorch profiler file without timestamp and ends with pt.trace.json + ( + ( + "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" + "aa1f87508336_37.pt.trace.json" + ), + 1, + ( + "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" + "aa1f87508336_37#1.pt.trace.json" + ), + ), + # Pytorch profiler file with timestamp and ends with pt.trace.json.gz + ( + ( + "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" + "aa1f87508336_37.1674696139174.pt.trace.json.gz" + ), + 1, + ( + "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" + "aa1f87508336_37#1.1674696139174.pt.trace.json.gz" + ), + ), + # Pytorch profiler file without timestamp and ends with pt.trace.json.gz + ( + ( + "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" + "aa1f87508336_37.pt.trace.json.gz" + ), + 1, + ( + "/tmp/tensorboard-39.ff54aea9-0a94-4ce7-bf38-e8b3e69cc944.1-0/" + "aa1f87508336_37#1.pt.trace.json.gz" + ), + ), + # Pytorch profiler file (only file name) with timestamp and ends with pt.trace.json.gz + ( + "aa1f87508336_37.1674696139174.pt.trace.json.gz", + 1, + "aa1f87508336_37#1.1674696139174.pt.trace.json.gz", + ), + # Pytorch profiler file (only file name) without timestamp and ends with pt.trace.json.gz + ( + "aa1f87508336_37.pt.trace.json.gz", + 1, + "aa1f87508336_37#1.pt.trace.json.gz", + ), ] diff --git a/harness/tests/test_imports.py b/harness/tests/test_imports.py index 8851753ae38..3f9ce118952 100644 --- a/harness/tests/test_imports.py +++ b/harness/tests/test_imports.py @@ -1,9 +1,18 @@ +import contextlib +import os +import pathlib +import shutil import subprocess import sys import textwrap +from typing import Iterator +import pytest -def test_imports() -> None: +import determined as det + + +def test_import_side_effects() -> None: # In a separate python process from pytest, import some common parts of # determined and ensure that no expensive imports are imported as side effects. script = """ @@ -52,3 +61,68 @@ def test_imports() -> None: assert not bad, bad """ subprocess.run([sys.executable, "-c", textwrap.dedent(script)], check=True) + + +def test_import_from_path() -> None: + @contextlib.contextmanager + def prepend_sys_path(path: str) -> Iterator: + old = sys.path + sys.path = [path] + sys.path + try: + yield + finally: + sys.path = old + + @contextlib.contextmanager + def chdir(path: pathlib.Path) -> Iterator: + old = os.getcwd() + os.chdir(str(path)) + try: + yield + finally: + os.chdir(old) + + fixture = pathlib.Path(__file__).parent / "fixtures" / "import_from_path" + # Modify sys.path so that lib1.py and lib2.py are treated as if it were an installed library. + # Installed libraries should not be affected by the caching behavior + with prepend_sys_path(str(fixture / "libraries")): + # Import lib1 before the checkpoints do. + import lib1 + + # Execute from the a/ directory like we were in a normal interactive interpreter. + with chdir(fixture / "a"), prepend_sys_path(""): + import model_def as a + + with det.import_from_path(fixture / "b"): + import model_def as b + + # Nesting is not supported. + with pytest.raises(RuntimeError, match="does not support nesting"): + with det.import_from_path(fixture / "c"): + pass + + with det.import_from_path(fixture / "c"): + import model_def as c + + # Import lib2 after the checkpoints do. + import lib2 + + # Each module should have its own val and data_val. + assert a.val == "a", a.val + assert a.data_val == "a", a.data_val + assert b.val == "b", b.val + assert b.data_val == "b", b.data_val + assert c.val == "c", c.val + assert c.data_val == "c", c.data_val + + # Each module must share the same lib1 and lib2, from outside import_from_path. + assert id(lib1) == a.lib1_id == b.lib1_id == c.lib1_id + assert id(lib2) == a.lib2_id == b.lib2_id == c.lib2_id + + # Ensure no cache files got created in the import_from_apaths + assert not os.path.exists(fixture / "b" / "__pycache__") + assert not os.path.exists(fixture / "c" / "__pycache__") + + # We will have created some __pycache__ dirs but we don't want to pollute. + shutil.rmtree(fixture / "a" / "__pycache__") + shutil.rmtree(fixture / "libraries" / "__pycache__") diff --git a/helm/charts/determined/Chart.yaml b/helm/charts/determined/Chart.yaml index 4f10f8ae2d9..3a89e4952f3 100644 --- a/helm/charts/determined/Chart.yaml +++ b/helm/charts/determined/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: determined description: A Helm chart for Determined -version: "0.19.12-dev0" +version: "0.20.1-dev0" icon: https://github.com/determined-ai/determined/blob/master/determined-logo.png?raw=true home: https://github.com/determined-ai/determined.git @@ -9,4 +9,4 @@ home: https://github.com/determined-ai/determined.git # a non-release version (e.g., X.Y.Z.dev0) you will have to specify an # existing official release version (e.g., X.Y.Z) or specify a commit has # that has been publicly published (all commits from master). -appVersion: "0.19.12-dev0" +appVersion: "0.20.1-dev0" diff --git a/helm/charts/determined/templates/master-config.yaml b/helm/charts/determined/templates/master-config.yaml index 98eced0c806..4966d63c9c1 100644 --- a/helm/charts/determined/templates/master-config.yaml +++ b/helm/charts/determined/templates/master-config.yaml @@ -131,8 +131,8 @@ data: {{- toYaml .Values.resourcePools | nindent 6}} {{- end }} - {{$cpuImage := (split "/" "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c")._1}} - {{- $gpuImage := (split "/" "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c")._1 -}} + {{$cpuImage := (split "/" "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364")._1}} + {{- $gpuImage := (split "/" "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364")._1 -}} {{ if .Values.taskContainerDefaults -}} task_container_defaults: {{- if .Values.taskContainerDefaults.networkMode }} diff --git a/helm/charts/determined/templates/master-permissions.yaml b/helm/charts/determined/templates/master-permissions.yaml index 0c5451575fd..27b29b50fe7 100644 --- a/helm/charts/determined/templates/master-permissions.yaml +++ b/helm/charts/determined/templates/master-permissions.yaml @@ -21,7 +21,7 @@ rules: resources: ["pods", "pods/status", "pods/log", "configmaps"] verbs: ["create", "get", "list", "delete"] - apiGroups: [""] - resources: ["services"] + resources: ["services", "resourcequotas"] verbs: ["get", "list"] - apiGroups: [""] resources: ["pods"] diff --git a/master/go.mod b/master/go.mod index 30c66e3800e..258ca45f0b4 100644 --- a/master/go.mod +++ b/master/go.mod @@ -40,7 +40,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.10.0 + github.com/prometheus/client_golang v1.11.1 github.com/santhosh-tekuri/jsonschema/v2 v2.2.0 github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect github.com/sirupsen/logrus v1.8.1 @@ -52,7 +52,7 @@ require ( github.com/uber/jaeger-client-go v2.25.0+incompatible github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 - golang.org/x/net v0.0.0-20211209124913-491a49abca63 + golang.org/x/net v0.7.0 google.golang.org/api v0.56.0 google.golang.org/grpc v1.45.0 google.golang.org/grpc/examples v0.0.0-20210525230658-4bae49e05b28 // indirect @@ -126,7 +126,7 @@ require ( github.com/pelletier/go-toml v1.9.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.25.0 // indirect + github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/shopspring/decimal v1.2.0 github.com/spf13/afero v1.6.0 // indirect @@ -148,9 +148,9 @@ require ( go.opentelemetry.io/proto/otlp v0.12.1 // indirect go.uber.org/atomic v1.9.0 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb diff --git a/master/go.sum b/master/go.sum index 4597c165c5a..f23ed884ae4 100644 --- a/master/go.sum +++ b/master/go.sum @@ -218,6 +218,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -673,8 +674,9 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -687,8 +689,9 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.25.0 h1:IjJYZJCI8HZYtqA3xYwGyDzSCy1r4CA2GRh+4vdOmtE= github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -986,8 +989,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1091,6 +1094,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1100,12 +1104,12 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1114,8 +1118,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/master/internal/api_auth.go b/master/internal/api_auth.go index 09ac8daa8b6..b759be3aab9 100644 --- a/master/internal/api_auth.go +++ b/master/internal/api_auth.go @@ -3,8 +3,10 @@ package internal import ( "context" "crypto/sha512" + "database/sql" "fmt" "net/http" + "strings" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -14,6 +16,7 @@ import ( "github.com/determined-ai/determined/master/internal/command" "github.com/determined-ai/determined/master/internal/db" + expauth "github.com/determined-ai/determined/master/internal/experiment" "github.com/determined-ai/determined/master/internal/grpcutil" "github.com/determined-ai/determined/master/internal/user" "github.com/determined-ai/determined/master/pkg/model" @@ -123,7 +126,7 @@ func processProxyAuthentication(c echo.Context) (done bool, err error) { return true, redirectToLogin(c) } - taskID := model.TaskID(c.Param("service")) + taskID := model.TaskID(strings.SplitN(c.Param("service"), ":", 2)[0]) var ctx context.Context if c.Request() == nil || c.Request().Context() == nil { @@ -133,10 +136,31 @@ func processProxyAuthentication(c echo.Context) (done bool, err error) { } spec, err := db.IdentifyTask(ctx, taskID) + if errors.Is(err, db.ErrNotFound) || errors.Cause(err) == sql.ErrNoRows { + // Check if it's an experiment. + e, err := db.ExperimentByTaskID(ctx, taskID) + if errors.Is(err, db.ErrNotFound) || errors.Cause(err) == sql.ErrNoRows { + return true, err + } + + if err != nil { + return true, fmt.Errorf("error looking up task experiment: %w", err) + } + + if ok, err := expauth.AuthZProvider.Get().CanGetExperiment(ctx, *user, e); err != nil { + return true, err + } else if !ok { + return true, echo.NewHTTPError(http.StatusNotFound, "service not found: "+taskID) + } + + return false, nil + } + if err != nil { - return true, err + return true, fmt.Errorf("error fetching task metadata: %w", err) } + // Continue NTSC task checks. var ok bool if spec.TaskType == model.TaskTypeTensorboard { ok, err = command.AuthZProvider.Get().CanGetTensorboard( diff --git a/master/internal/api_experiment.go b/master/internal/api_experiment.go index 10fc5e9e63f..5acbd76996c 100644 --- a/master/internal/api_experiment.go +++ b/master/internal/api_experiment.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "math" - "regexp" "sort" "strconv" "strings" @@ -494,7 +493,7 @@ func (a *apiServer) GetExperiments( apiv1.GetExperimentsRequest_SORT_BY_CHECKPOINT_SIZE: "checkpoint_size", apiv1.GetExperimentsRequest_SORT_BY_CHECKPOINT_COUNT: "checkpoint_count", apiv1.GetExperimentsRequest_SORT_BY_SEARCHER_METRIC_VAL: `( - SELECT + SELECT searcher_metric_value FROM trials t WHERE t.experiment_id = e.id @@ -1298,74 +1297,6 @@ func (a *apiServer) MetricNames(req *apiv1.MetricNamesRequest, } } -// DEPRECATED -- do not use. -func (a *apiServer) ExpCompareMetricNames(req *apiv1.ExpCompareMetricNamesRequest, - resp apiv1.Determined_ExpCompareMetricNamesServer, -) error { - seenTrain := make(map[string]bool) - seenValid := make(map[string]bool) - var tStartTime time.Time - var vStartTime time.Time - period := time.Duration(req.PeriodSeconds) * time.Second - if period == 0 { - period = defaultMetricsStreamPeriod - } - if len(req.TrialId) == 0 { - return status.Errorf( - codes.InvalidArgument, - "at least one trial id required", - ) - } - - var timeSinceLastAuth time.Time - for { - if time.Now().Sub(timeSinceLastAuth) >= recheckAuthPeriod { - for _, trialID := range req.TrialId { - if err := a.canGetTrialsExperimentAndCheckCanDoAction(resp.Context(), int(trialID), - expauth.AuthZProvider.Get().CanGetExperimentArtifacts); err != nil { - return err - } - } - timeSinceLastAuth = time.Now() - } - - var response apiv1.ExpCompareMetricNamesResponse - - newTrain, newValid, tEndTime, vEndTime, err := a.m.db.ExpCompareMetricNames(req.TrialId, - tStartTime, vStartTime) - if err != nil { - return err - } - tStartTime = tEndTime - vStartTime = vEndTime - - for _, name := range newTrain { - if seen := seenTrain[name]; !seen { - response.TrainingMetrics = append(response.TrainingMetrics, name) - seenTrain[name] = true - } - } - for _, name := range newValid { - if seen := seenValid[name]; !seen { - response.ValidationMetrics = append(response.ValidationMetrics, name) - seenValid[name] = true - } - } - - if grpcutil.ConnectionIsClosed(resp) { - return nil - } - if err = resp.Send(&response); err != nil { - return err - } - - time.Sleep(period) - if grpcutil.ConnectionIsClosed(resp) { - return nil - } - } -} - func (a *apiServer) MetricBatches(req *apiv1.MetricBatchesRequest, resp apiv1.Determined_MetricBatchesServer, ) error { @@ -1632,66 +1563,6 @@ func (a *apiServer) fetchTrialSample(trialID int32, metricName string, metricTyp return &trial, nil } -// DEPRECATED -- do not use. -func (a *apiServer) expCompareFetchTrialSample(trialID int32, metricName string, - metricType apiv1.MetricType, maxDatapoints int, startBatches int, endBatches int, - currentTrials map[int32]bool, - trialCursors map[int32]time.Time, -) (*apiv1.ExpCompareTrialsSampleResponse_ExpTrial, error) { - var metricSeries []lttb.Point - var endTime time.Time - var zeroTime time.Time - var err error - var trial apiv1.ExpCompareTrialsSampleResponse_ExpTrial - var metricMeasurements db.MetricMeasurements - var xAxisLabelMetrics []string - trial.TrialId = trialID - - if _, current := currentTrials[trialID]; !current { - var trialConfig *model.Trial - trialConfig, err = a.m.db.TrialByID(int(trialID)) - if err != nil { - return nil, errors.Wrapf(err, "error fetching trial metadata") - } - trial.Hparams = protoutils.ToStruct(trialConfig.HParams) - trial.ExperimentId = int32(trialConfig.ExperimentID) - } - - startTime, seenBefore := trialCursors[trialID] - if !seenBefore { - startTime = zeroTime - } - switch metricType { - case apiv1.MetricType_METRIC_TYPE_TRAINING: - metricMeasurements, err = a.m.db.TrainingMetricsSeries(trialID, startTime, - metricName, startBatches, endBatches, xAxisLabelMetrics) - case apiv1.MetricType_METRIC_TYPE_VALIDATION: - metricMeasurements, err = a.m.db.ValidationMetricsSeries(trialID, startTime, - metricName, startBatches, endBatches, xAxisLabelMetrics) - default: - panic("Invalid metric type") - } - if err != nil { - return nil, errors.Wrapf(err, "error fetching time series of metrics") - } - if len(metricMeasurements.Batches) > 0 { - // if we get empty results, the endTime is incorrectly zero - trialCursors[trialID] = endTime - } - if !seenBefore { - metricSeries = lttb.Downsample(metricMeasurements.Batches, maxDatapoints, false) - } - - for _, in := range metricSeries { - out := apiv1.DataPoint{ - Batches: int32(in.X), - Value: in.Y, - } - trial.Data = append(trial.Data, &out) - } - return &trial, nil -} - func (a *apiServer) TrialsSample(req *apiv1.TrialsSampleRequest, resp apiv1.Determined_TrialsSampleServer, ) error { @@ -1805,114 +1676,6 @@ func (a *apiServer) TrialsSample(req *apiv1.TrialsSampleRequest, } } -func (a *apiServer) ExpCompareTrialsSample(req *apiv1.ExpCompareTrialsSampleRequest, - resp apiv1.Determined_ExpCompareTrialsSampleServer, -) error { - experimentIDs := req.ExperimentIds - maxTrials := int(req.MaxTrials) - if maxTrials == 0 { - maxTrials = 25 - } - maxDatapoints := int(req.MaxDatapoints) - if maxDatapoints == 0 { - maxDatapoints = 1000 - } - startBatches := int(req.StartBatches) - endBatches := int(req.EndBatches) - if endBatches <= 0 { - endBatches = math.MaxInt32 - } - period := time.Duration(req.PeriodSeconds) * time.Second - if period == 0 { - period = defaultMetricsStreamPeriod - } - - metricName := req.MetricName - metricType := req.MetricType - if metricType == apiv1.MetricType_METRIC_TYPE_UNSPECIFIED { - return status.Error(codes.InvalidArgument, "must specify a metric type") - } - if metricName == "" { - return status.Error(codes.InvalidArgument, "must specify a metric name") - } - - var timeSinceLastAuth time.Time - trialCursors := make(map[int32]time.Time) - currentTrials := make(map[int32]bool) - for { - if time.Now().Sub(timeSinceLastAuth) >= recheckAuthPeriod { - for _, expID := range experimentIDs { - if _, _, err := a.getExperimentAndCheckCanDoActions(resp.Context(), int(expID), - expauth.AuthZProvider.Get().CanGetExperimentArtifacts); err != nil { - return err - } - } - timeSinceLastAuth = time.Now() - } - - var response apiv1.ExpCompareTrialsSampleResponse - var promotedTrials []int32 - var demotedTrials []int32 - var trials []*apiv1.ExpCompareTrialsSampleResponse_ExpTrial - - seenThisRound := make(map[int32]bool) - - r := regexp.MustCompile("(?i)(error|loss|mse|mae|mse|deviation|false)") - smallerIsBetter := r.MatchString(metricName) - - trialIDs, err := a.m.db.ExpCompareTopTrialsByMetric(experimentIDs, - maxTrials, - metricName, - smallerIsBetter) - if err != nil { - return err - } - - for _, trialID := range trialIDs { - var trial *apiv1.ExpCompareTrialsSampleResponse_ExpTrial - trial, err = a.expCompareFetchTrialSample(trialID, metricName, metricType, maxDatapoints, - startBatches, endBatches, currentTrials, trialCursors) - if err != nil { - return err - } - - if _, current := currentTrials[trialID]; !current { - promotedTrials = append(promotedTrials, trialID) - currentTrials[trialID] = true - } - seenThisRound[trialID] = true - - trials = append(trials, trial) - } - for oldTrial := range currentTrials { - if !seenThisRound[oldTrial] { - demotedTrials = append(demotedTrials, oldTrial) - delete(trialCursors, oldTrial) - } - } - // Deletes from currentTrials have to happen when not looping over currentTrials - for _, oldTrial := range demotedTrials { - delete(currentTrials, oldTrial) - } - - response.Trials = trials - response.PromotedTrials = promotedTrials - response.DemotedTrials = demotedTrials - - if grpcutil.ConnectionIsClosed(resp) { - return nil - } - if err = resp.Send(&response); err != nil { - return errors.Wrap(err, "error sending sample of trial metric streams") - } - - time.Sleep(period) - if grpcutil.ConnectionIsClosed(resp) { - return nil - } - } -} - func (a *apiServer) ComputeHPImportance(ctx context.Context, req *apiv1.ComputeHPImportanceRequest, ) (*apiv1.ComputeHPImportanceResponse, error) { diff --git a/master/internal/api_experiment_intg_test.go b/master/internal/api_experiment_intg_test.go index dd2856e8b65..999da465fb6 100644 --- a/master/internal/api_experiment_intg_test.go +++ b/master/internal/api_experiment_intg_test.go @@ -891,36 +891,6 @@ func TestAuthZCreateExperiment(t *testing.T) { require.Equal(t, expectedErr, err) } -func TestAuthZExpCompareTrialsSample(t *testing.T) { - api, authZExp, _, curUser, ctx := setupExpAuthTest(t, nil) - - exp0 := createTestExp(t, api, curUser) - exp1 := createTestExp(t, api, curUser) - req := &apiv1.ExpCompareTrialsSampleRequest{ - ExperimentIds: []int32{int32(exp0.ID), int32(exp1.ID)}, - MetricName: "name", - MetricType: apiv1.MetricType_METRIC_TYPE_TRAINING, - } - - // Can't view first experiment gets error. - expectedErr := status.Errorf(codes.PermissionDenied, "firstError") - authZExp.On("CanGetExperiment", mock.Anything, curUser, exp0).Return(true, nil).Once() - authZExp.On("CanGetExperimentArtifacts", mock.Anything, curUser, exp0). - Return(fmt.Errorf("firstError")).Once() - err := api.ExpCompareTrialsSample(req, mockStream[*apiv1.ExpCompareTrialsSampleResponse]{ctx}) - require.Equal(t, expectedErr.Error(), err.Error()) - - // Can't view second experiment gets error. - expectedErr = status.Errorf(codes.PermissionDenied, "secondError") - authZExp.On("CanGetExperiment", mock.Anything, curUser, exp0).Return(true, nil).Once() - authZExp.On("CanGetExperimentArtifacts", mock.Anything, curUser, exp0).Return(nil).Once() - authZExp.On("CanGetExperiment", mock.Anything, curUser, exp1).Return(true, nil).Once() - authZExp.On("CanGetExperimentArtifacts", mock.Anything, curUser, exp1). - Return(fmt.Errorf("secondError")).Once() - err = api.ExpCompareTrialsSample(req, mockStream[*apiv1.ExpCompareTrialsSampleResponse]{ctx}) - require.Equal(t, expectedErr.Error(), err.Error()) -} - func TestAuthZGetExperimentAndCanDoActions(t *testing.T) { api, authZExp, _, curUser, ctx := setupExpAuthTest(t, nil) exp := createTestExp(t, api, curUser) diff --git a/master/internal/api_notebook.go b/master/internal/api_notebook.go index ce0e0134c90..a8e29d4f94f 100644 --- a/master/internal/api_notebook.go +++ b/master/internal/api_notebook.go @@ -264,8 +264,10 @@ func (a *apiServer) LaunchNotebook( "NOTEBOOK_IDLE_TYPE": spec.Config.NotebookIdleType, "DET_TASK_TYPE": string(model.TaskTypeNotebook), } - spec.Port = &port - spec.Config.Environment.Ports = map[string]int{"notebook": port} + spec.Base.ExtraProxyPorts = append(spec.Base.ExtraProxyPorts, expconf.ProxyPort{ + RawProxyPort: port, + RawDefaultServiceID: ptrs.Ptr(true), + }) spec.Config.Entrypoint = []string{jupyterEntrypoint} diff --git a/master/internal/api_project.go b/master/internal/api_project.go index ef94b39d79a..f4a74dd6f2f 100644 --- a/master/internal/api_project.go +++ b/master/internal/api_project.go @@ -364,29 +364,37 @@ func (a *apiServer) GetProjectsByUserActivity( } err = db.Bun().NewSelect().Model(p).NewRaw(` - WITH p as ( - SELECT pr.*, activity_time FROM projects pr - JOIN activity a ON pr.id = a.entity_id WHERE a.user_id = ? ORDER BY a.activity_time DESC LIMIT ? - ), - pe AS ( - SELECT project_id, state, start_time - FROM experiments - ) - SELECT w.name as workspace_name, u.username, p.id, p.name, - p.workspace_id, p.description, p.immutable, p.notes, p.user_id, - 'WORKSPACE_STATE_' || p.state AS state, p.error_message, - SUM(case when pe.project_id = p.id then 1 else 0 end) AS num_experiments, - SUM(case when pe.project_id = p.id - AND pe.state = 'ACTIVE' then 1 else 0 end) AS num_active_experiments, - MAX(case when pe.project_id = p.id then pe.start_time else NULL end) - AS last_experiment_started_at - FROM p - LEFT JOIN users as u ON u.id = p.user_id - LEFT JOIN workspaces AS w on w.id = p.workspace_id - LEFT JOIN pe on pe.project_id = p.id - GROUP BY p.user_id, p.id, p.name, p.workspace_id, p.description, - p.immutable, p.notes, p.state, p.error_message, u.username, w.name, p.activity_time - ORDER BY p.activity_time DESC;`, curUser.ID, limit). + SELECT + w.name AS workspace_name, + u.username, + p.id, + p.name, + p.archived, + p.workspace_id, + p.description, + p.immutable, + p.notes, + p.user_id, + 'WORKSPACE_STATE_' || p.state AS state, + p.error_message, + COUNT(*) FILTER (WHERE e.project_id = p.id) AS num_experiments, + COUNT(*) FILTER (WHERE e.project_id = p.id AND e.state = 'ACTIVE') AS num_active_experiments, + MAX(e.start_time) FILTER (WHERE e.project_id = p.id) AS last_experiment_started_at + FROM + projects AS p + INNER JOIN activity AS a ON p.id = a.entity_id AND a.user_id = ? + LEFT JOIN users AS u ON u.id = p.user_id + LEFT JOIN workspaces AS w ON w.id = p.workspace_id + LEFT JOIN experiments AS e ON e.project_id = p.id + GROUP BY + p.id, + u.username, + w.name, + a.activity_time + ORDER BY + a.activity_time DESC NULLS LAST + LIMIT ?; + `, curUser.ID, limit). Scan(ctx, &p) if err != nil { return nil, err diff --git a/master/internal/api_shell.go b/master/internal/api_shell.go index 05cf0e135fb..21ace0c5c6c 100644 --- a/master/internal/api_shell.go +++ b/master/internal/api_shell.go @@ -203,8 +203,13 @@ func (a *apiServer) LaunchShell( // Selecting a random port mitigates the risk of multiple processes binding // the same port on an agent in host mode. port := getRandomPort(minSshdPort, maxSshdPort) - spec.Port = &port - spec.Config.Environment.Ports = map[string]int{"shell": port} + // Shell authentication happens through SSH keys, instead. + spec.Base.ExtraProxyPorts = append(spec.Base.ExtraProxyPorts, expconf.ProxyPort{ + RawProxyPort: port, + RawProxyTCP: ptrs.Ptr(true), + RawUnauthenticated: ptrs.Ptr(true), + RawDefaultServiceID: ptrs.Ptr(true), + }) spec.Config.Entrypoint = []string{ shellEntrypointScript, "-f", shellSSHDConfigFile, "-p", strconv.Itoa(port), "-D", "-e", @@ -252,10 +257,6 @@ func (a *apiServer) LaunchShell( spec.Metadata.PublicKey = ptrs.Ptr(string(keys.PublicKey)) spec.Keys = &keys - spec.ProxyTCP = true - // Shell authentication happens through SSH keys, instead. - spec.Unauthenticated = true - // Launch a Shell actor. var shellID model.TaskID if err := a.ask(shellsAddr, *spec, &shellID); err != nil { diff --git a/master/internal/api_tensorboard.go b/master/internal/api_tensorboard.go index 6116e24d130..f4954501f6d 100644 --- a/master/internal/api_tensorboard.go +++ b/master/internal/api_tensorboard.go @@ -245,8 +245,10 @@ func (a *apiServer) LaunchTensorboard( // Selecting a random port mitigates the risk of multiple processes binding // the same port on an agent in host mode. port := getRandomPort(minTensorBoardPort, maxTensorBoardPort) - spec.Port = &port - spec.Config.Environment.Ports = map[string]int{"tensorboard": port} + spec.Base.ExtraProxyPorts = append(spec.Base.ExtraProxyPorts, expconf.ProxyPort{ + RawProxyPort: port, + RawDefaultServiceID: ptrs.Ptr(true), + }) spec.Metadata.ExperimentIDs = req.ExperimentIds spec.Metadata.TrialIDs = req.TrialIds diff --git a/master/internal/api_trials_intg_test.go b/master/internal/api_trials_intg_test.go index 5aa804943c7..b3f9fb9f093 100644 --- a/master/internal/api_trials_intg_test.go +++ b/master/internal/api_trials_intg_test.go @@ -169,11 +169,6 @@ func TestTrialAuthZ(t *testing.T) { }) return err }, false}, - {"CanGetExperimentArtifacts", func(id int) error { - return api.ExpCompareMetricNames(&apiv1.ExpCompareMetricNamesRequest{ - TrialId: []int32{int32(id)}, - }, mockStream[*apiv1.ExpCompareMetricNamesResponse]{ctx}) - }, false}, {"CanGetExperimentArtifacts", func(id int) error { _, err := api.LaunchTensorboard(ctx, &apiv1.LaunchTensorboardRequest{ TrialIds: []int32{int32(id)}, diff --git a/master/internal/checkpoint_gc.go b/master/internal/checkpoint_gc.go index c02c1b41dd4..744483bd0bc 100644 --- a/master/internal/checkpoint_gc.go +++ b/master/internal/checkpoint_gc.go @@ -96,6 +96,11 @@ func (t *checkpointGCTask) Receive(ctx *actor.Context) error { t.allocationID = model.AllocationID(fmt.Sprintf("%s.%d", t.taskID, 1)) + rp, err := t.rm.ResolveResourcePool(ctx, "", 0) + if err != nil { + return fmt.Errorf("resolving resource pool: %w", err) + } + allocation := task.NewAllocation(t.logCtx, sproto.AllocateRequest{ TaskID: t.taskID, JobID: t.jobID, @@ -106,6 +111,7 @@ func (t *checkpointGCTask) Receive(ctx *actor.Context) error { SingleAgent: true, }, AllocationRef: ctx.Self(), + ResourcePool: rp, }, t.db, t.rm, t.taskLogger) t.allocation, _ = ctx.ActorOf(t.allocationID, allocation) diff --git a/master/internal/command/command.go b/master/internal/command/command.go index 39d730cf6fa..f6869f51d56 100644 --- a/master/internal/command/command.go +++ b/master/internal/command/command.go @@ -265,16 +265,6 @@ func (c *command) Receive(ctx *actor.Context) error { } } - var proxyPortConf *sproto.ProxyPortConfig - if c.GenericCommandSpec.Port != nil { - proxyPortConf = &sproto.ProxyPortConfig{ - ServiceID: string(c.taskID), - Port: *c.GenericCommandSpec.Port, - ProxyTCP: c.ProxyTCP, - Unauthenticated: c.Unauthenticated, - } - } - c.eventStream, _ = ctx.ActorOf("events", newEventManager(c.Config.Description)) var eventStreamConfig *sproto.EventStreamConfig @@ -312,7 +302,7 @@ func (c *command) Receive(ctx *actor.Context) error { }, StreamEvents: eventStreamConfig, - ProxyPort: proxyPortConf, + ProxyPorts: sproto.NewProxyPortConfig(c.GenericCommandSpec.ProxyPorts(), c.taskID), IdleTimeout: idleWatcherConfig, Restore: c.restored, }, c.db, c.rm, c.taskLogger) diff --git a/master/internal/config/provconfig/aws_config.go b/master/internal/config/provconfig/aws_config.go index fc69d367ee0..20db914417f 100644 --- a/master/internal/config/provconfig/aws_config.go +++ b/master/internal/config/provconfig/aws_config.go @@ -49,16 +49,16 @@ type AWSClusterConfig struct { } var defaultAWSImageID = map[string]string{ - "ap-northeast-1": "ami-0efbc837b3c729df1", - "ap-northeast-2": "ami-0934d35fc17d76abc", - "ap-southeast-1": "ami-05068dcfa829e229e", - "ap-southeast-2": "ami-0e62ea531c3969a65", - "us-east-2": "ami-0ba898a163ad80bf9", - "us-east-1": "ami-0b4cc2ed4ecd07a2d", - "us-west-2": "ami-0a483013f2e53ed60", - "eu-central-1": "ami-051a91b2829200200", - "eu-west-2": "ami-08aa94155b641d19d", - "eu-west-1": "ami-04eab4dc55258e621", + "ap-northeast-1": "ami-09c9ea05363b4fe2d", + "ap-northeast-2": "ami-07b13ec47230370d5", + "ap-southeast-1": "ami-07affc5a669acb69f", + "ap-southeast-2": "ami-0ef38d26d9f61a0db", + "us-east-2": "ami-0fe46b7b5fdba7b51", + "us-east-1": "ami-027145fb15545cd96", + "us-west-2": "ami-03bad0b25097fe67d", + "eu-central-1": "ami-0c09eb6495c53c53c", + "eu-west-2": "ami-0130ec158c7260bf1", + "eu-west-1": "ami-06de67607d7b543dd", } var defaultAWSClusterConfig = AWSClusterConfig{ diff --git a/master/internal/config/provconfig/gcp_config.go b/master/internal/config/provconfig/gcp_config.go index 3dd51db840d..dba5f8e6751 100644 --- a/master/internal/config/provconfig/gcp_config.go +++ b/master/internal/config/provconfig/gcp_config.go @@ -53,7 +53,7 @@ type GCPClusterConfig struct { func DefaultGCPClusterConfig() *GCPClusterConfig { return &GCPClusterConfig{ BootDiskSize: 200, - BootDiskSourceImage: "projects/determined-ai/global/images/det-environments-ad0591c", + BootDiskSourceImage: "projects/determined-ai/global/images/det-environments-7aa5364", LabelKey: "managed-by", InstanceType: gceInstanceType{ MachineType: "n1-standard-32", diff --git a/master/internal/core.go b/master/internal/core.go index 34a3c1f7e92..fdbcd4266c0 100644 --- a/master/internal/core.go +++ b/master/internal/core.go @@ -314,12 +314,210 @@ func (m *Master) fetchAggregatedResourceAllocation( } } +// TaskMetadata captures the historic allocation information for a given task. +type TaskMetadata struct { + TaskID model.TaskID + TaskType model.TaskType + Username string + WorkspaceName string + ExperimentID int + Slots int + StartTime time.Time + EndTime time.Time + ImagepullingTime float64 +} + +// @Summary Get a detailed view of resource allocation at a task-level during the given time period (CSV). +// @Tags Cluster +// @ID get-raw-resource-task-allocation-csv +// @Accept json +// @Produce text/csv +// +// nolint:lll +// +// @Param timestamp_after query string true "Start time to get allocations for (YYYY-MM-DDTHH:MM:SSZ format)" +// +// nolint:lll +// +// @Param timestamp_before query string true "End time to get allocations for (YYYY-MM-DDTHH:MM:SSZ format)" +// +// nolint:lll +// +// @Success 200 {} string "A CSV file containing the fields task_id, task_type, username, workspace_name, experiment_id, slots, start_time, end_time, training_time, validation_time, checkpointing_time, imagepulling_time" +// @Router /allocations/tasks-raw [get] +func (m *Master) getRawResourceAllocationTasks(c echo.Context) error { + // Get start and end times from context + args := struct { + Start string `query:"timestamp_after"` + End string `query:"timestamp_before"` + }{} + if err := api.BindArgs(&args, c); err != nil { + return err + } + + // Parse start & end timestamps + start, err := time.Parse("2006-01-02T15:04:05Z", args.Start) + if err != nil { + return errors.Wrap(err, "invalid start time") + } + end, err := time.Parse("2006-01-02T15:04:05Z", args.End) + if err != nil { + return errors.Wrap(err, "invalid end time") + } + if start.After(end) { + return errors.New("start time cannot be after end time") + } + + // Get task info for tasks in time range + tasksInRange := db.Bun().NewSelect(). + ColumnExpr("t.task_id"). + ColumnExpr("t.task_type"). + ColumnExpr("t.start_time"). + ColumnExpr("t.end_time"). + ColumnExpr("t.job_id"). + TableExpr("tasks t"). + Where("tstzrange(start_time, end_time) && tstzrange(? :: timestamptz, ? :: timestamptz)", start, end) + + // Get allocation info for allocations in time range + allocationsInRange := db.Bun().NewSelect(). + ColumnExpr("a.task_id"). + ColumnExpr("a.allocation_id"). + ColumnExpr("a.start_time"). + ColumnExpr("a.end_time"). + ColumnExpr("a.slots"). + TableExpr("allocations a"). + Where("tstzrange(start_time, end_time) && tstzrange(? :: timestamptz, ? :: timestamptz)", start, end) + + // Get the owner usernames associated with each task_id + taskOwners := db.Bun().NewSelect(). + ColumnExpr("t.task_id"). + ColumnExpr("u.username"). + TableExpr("tasks_in_range t"). + Join("INNER JOIN jobs j ON t.job_id = j.job_id"). + Join("INNER JOIN users u ON j.owner_id = u.id") + + // Get the number of slots request for a given task + taskSlots := db.Bun().NewSelect(). + ColumnExpr("a.task_id"). + ColumnExpr("(array_agg(a.slots) FILTER (WHERE a.slots IS NOT NULL))[1] as slots"). + TableExpr("allocations_in_range a"). + Group("a.task_id") + + // Get imagepull times for tasks within time range + imagePullTimes := db.Bun().NewSelect(). + ColumnExpr("a.task_id"). + ColumnExpr("SUM(EXTRACT(EPOCH FROM (ts.end_time - ts.start_time))) imagepulling_time"). + TableExpr("allocations_in_range a"). + Join("INNER JOIN task_stats ts ON a.allocation_id = ts.allocation_id"). + Where("ts.event_type = 'IMAGEPULL'"). + Group("a.task_id") + + // Get experiment info for tasks within time range + taskExperimentInfo := db.Bun().NewSelect(). + ColumnExpr("t.task_id"). + ColumnExpr("e.id as experiment_id"). + ColumnExpr("w.name as workspace_name"). + TableExpr("tasks_in_range t"). + Join("INNER JOIN experiments e ON t.job_id = e.job_id"). + Join("INNER JOIN projects p ON e.project_id = p.id"). + Join("INNER JOIN workspaces w ON p.workspace_id = w.id") + + // Get task information row-by-row for all tasks in time range + rows, err := db.Bun().NewSelect(). + ColumnExpr("t.task_id AS task_id"). + ColumnExpr("t.task_type AS task_type"). + ColumnExpr("t_o.username AS username"). + ColumnExpr("tei.workspace_name"). + ColumnExpr("tei.experiment_id"). + ColumnExpr("ts.slots as slots"). + ColumnExpr("t.start_time AS start_time"). + ColumnExpr("t.end_time AS end_time"). + ColumnExpr("ip.imagepulling_time"). + With("tasks_in_range", tasksInRange). + With("allocations_in_range", allocationsInRange). + With("task_slots", taskSlots). + With("task_owners", taskOwners). + With("image_pull_times", imagePullTimes). + With("task_experiment_info", taskExperimentInfo). + TableExpr("tasks_in_range t"). + Join("LEFT JOIN task_slots ts ON ts.task_id = t.task_id"). + Join("LEFT JOIN task_owners t_o ON t_o.task_id = t.task_id"). + Join("LEFT JOIN task_experiment_info tei ON tei.task_id = t.task_id"). + Join("LEFT JOIN image_pull_times ip ON ip.task_id = t.task_id"). + Order("t.start_time"). + Rows(c.Request().Context()) + + if err != nil && rows.Err() != nil { + return err + } + defer rows.Close() + + c.Response().Header().Set("Content-Type", "text/csv") + header := []string{ + "task_id", + "task_type", + "username", + "workspace_name", + "experiment_id", + "slots", + "start_time", + "end_time", + "imagepulling_time", + } + + formatTimestamp := func(t time.Time) string { + if t.IsZero() { + return "" + } + return t.Format(time.RFC3339Nano) + } + + formatDuration := func(duration float64) string { + if duration == 0 { + return "0.0" + } + return fmt.Sprintf("%f", duration) + } + + csvWriter := csv.NewWriter(c.Response()) + if err = csvWriter.Write(header); err != nil { + return err + } + + // Write each entry to the output CSV + for rows.Next() { + taskMetadata := new(TaskMetadata) + if err := db.Bun().ScanRow(c.Request().Context(), rows, taskMetadata); err != nil { + return err + } + fields := []string{ + taskMetadata.TaskID.String(), + string(taskMetadata.TaskType), + taskMetadata.Username, + taskMetadata.WorkspaceName, + strconv.Itoa(taskMetadata.ExperimentID), + strconv.Itoa(taskMetadata.Slots), + formatTimestamp(taskMetadata.StartTime), + formatTimestamp(taskMetadata.EndTime), + formatDuration(taskMetadata.ImagepullingTime), + } + if err := csvWriter.Write(fields); err != nil { + return err + } + } + csvWriter.Flush() + return nil +} + // @Summary Get an aggregated view of resource allocation during the given time period (CSV). // @Tags Cluster // @ID get-aggregated-resource-allocation-csv // @Produce text/csv // @Param start_date query string true "Start time to get allocations for (YYYY-MM-DD format for daily, YYYY-MM format for monthly)" // @Param end_date query string true "End time to get allocations for (YYYY-MM-DD format for daily, YYYY-MM format for monthly)" +// +// nolint:lll +// // @Param period query string true "Period to aggregate over (RESOURCE_ALLOCATION_AGGREGATION_PERIOD_DAILY or RESOURCE_ALLOCATION_AGGREGATION_PERIOD_MONTHLY)" // @Success 200 {} string "aggregation_type,aggregation_key,date,seconds" // @Router /allocation/aggregated [get] @@ -966,6 +1164,7 @@ func (m *Master) Run(ctx context.Context) error { resourcesGroup := m.echo.Group("/resources") resourcesGroup.GET("/allocation/raw", m.getRawResourceAllocation) + resourcesGroup.GET("/allocation/tasks-raw", m.getRawResourceAllocationTasks) resourcesGroup.GET("/allocation/aggregated", m.getAggregatedResourceAllocation) m.echo.POST("/task-logs", api.Route(m.postTaskLogs)) diff --git a/master/internal/core_experiment.go b/master/internal/core_experiment.go index 009ded7753d..67549dc44f3 100644 --- a/master/internal/core_experiment.go +++ b/master/internal/core_experiment.go @@ -563,6 +563,12 @@ func (m *Master) parseCreateExperiment(params *CreateExperimentParams, user *mod dbExp.Username = user.Username } + taskSpec.Project = config.Project() + taskSpec.Workspace = config.Workspace() + for label := range config.Labels() { + taskSpec.Labels = append(taskSpec.Labels, label) + } + return dbExp, config, project, params.ValidateOnly, &taskSpec, err } diff --git a/master/internal/db/database.go b/master/internal/db/database.go index 4b81d256bc8..26075a8c2d4 100644 --- a/master/internal/db/database.go +++ b/master/internal/db/database.go @@ -106,8 +106,6 @@ type DB interface { err error) MetricNames(experimentID int, sStartTime time.Time, vStartTime time.Time) ( training []string, validation []string, sEndTime time.Time, vEndTime time.Time, err error) - ExpCompareMetricNames(trialIDs []int32, sStartTime time.Time, vStartTime time.Time) ( - training []string, validation []string, sEndTime time.Time, vEndTime time.Time, err error) TrainingMetricBatches(experimentID int, metricName string, startTime time.Time) ( batches []int32, endTime time.Time, err error) ValidationMetricBatches(experimentID int, metricName string, startTime time.Time) ( @@ -120,8 +118,6 @@ type DB interface { endTime time.Time, err error) TopTrialsByMetric(experimentID int, maxTrials int, metric string, smallerIsBetter bool) (trials []int32, err error) - ExpCompareTopTrialsByMetric(experimentID []int32, maxTrials int, metric string, - smallerIsBetter bool) (trials []int32, err error) TopTrialsByTrainingLength(experimentID int, maxTrials int, metric string, smallerIsBetter bool) (trials []int32, err error) TrainingMetricsSeries(trialID int32, startTime time.Time, metricName string, diff --git a/master/internal/db/migrations.go b/master/internal/db/migrations.go index 93b324c19bb..a361489a6cd 100644 --- a/master/internal/db/migrations.go +++ b/master/internal/db/migrations.go @@ -132,6 +132,7 @@ func (db *PgDB) Migrate(migrationURL string, actions []string) error { if err != nil { return err } + defer func() { // Rollback unless it has already been committed. if errd := tx.Close(); errd != nil { @@ -139,6 +140,17 @@ func (db *PgDB) Migrate(migrationURL string, actions []string) error { } }() + // In integration tests, multiple processes can be running this code at once, which can lead to + // errors because PostgreSQL's CREATE TABLE IF NOT EXISTS is not great with concurrency. + + // Arbitrarily chosen unique consistent ID for the lock. + const MigrationLockID = 0x33ad0708c9bed25b + + _, err = tx.Exec("SELECT pg_advisory_xact_lock(?)", MigrationLockID) + if err != nil { + return err + } + if err = ensureMigrationUpgrade(tx); err != nil { return errors.Wrap(err, "error upgrading migration metadata") } diff --git a/master/internal/db/postgres_experiments.go b/master/internal/db/postgres_experiments.go index f495294b1a7..c8f1049e51b 100644 --- a/master/internal/db/postgres_experiments.go +++ b/master/internal/db/postgres_experiments.go @@ -172,59 +172,6 @@ GROUP BY name`, &rows, experimentID, vStartTime) return training, validation, sEndTime, vEndTime, err } -// ExpCompareMetricNames returns the set of training and validation metric names -// that have been recorded for a list of trials. -func (db *PgDB) ExpCompareMetricNames(trialIDs []int32, sStartTime time.Time, - vStartTime time.Time) (training []string, validation []string, sEndTime time.Time, - vEndTime time.Time, err error, -) { - type namesWrapper struct { - Name string `db:"name"` - EndTime time.Time `db:"end_time"` - } - var rows []namesWrapper - err = db.queryRows(` -SELECT - jsonb_object_keys(s.metrics->'avg_metrics') AS name, - max(s.end_time) AS end_time -FROM trials t -JOIN steps s ON t.id=s.trial_id -WHERE t.id IN (SELECT unnest($1::int [])::int) - AND s.end_time > $2 -GROUP BY name`, &rows, trialIDs, sStartTime) - if err != nil { - return nil, nil, sEndTime, vEndTime, errors.Wrapf(err, - "error querying training metric names fort rials") - } - for _, row := range rows { - training = append(training, row.Name) - if row.EndTime.After(sEndTime) { - sEndTime = row.EndTime - } - } - - err = db.queryRows(` -SELECT - jsonb_object_keys(v.metrics->'validation_metrics') AS name, - max(v.end_time) AS end_time -FROM trials t -JOIN validations v ON t.id = v.trial_id -WHERE t.id IN (SELECT unnest($1::int [])::int) - AND v.end_time > $2 -GROUP BY name`, &rows, trialIDs, vStartTime) - if err != nil { - return nil, nil, sEndTime, vEndTime, errors.Wrapf(err, - "error querying validation metric names for trials") - } - for _, row := range rows { - validation = append(validation, row.Name) - if row.EndTime.After(sEndTime) { - sEndTime = row.EndTime - } - } - return training, validation, sEndTime, vEndTime, err -} - type batchesWrapper struct { Batches int32 `db:"batches_processed"` EndTime time.Time `db:"end_time"` @@ -241,7 +188,6 @@ SELECT s.total_batches AS batches_processed, max(s.end_time) as end_time FROM trials t INNER JOIN steps s ON t.id=s.trial_id WHERE t.experiment_id=$1 - AND s.state = 'COMPLETED' AND s.metrics->'avg_metrics' ? $2 AND s.end_time > $3 GROUP BY batches_processed;`, &rows, experimentID, metricName, startTime) @@ -271,7 +217,6 @@ SELECT FROM trials t JOIN validations v ON t.id = v.trial_id WHERE t.experiment_id=$1 - AND v.state = 'COMPLETED' AND v.metrics->'validation_metrics' ? $2 AND v.end_time > $3 GROUP BY batches_processed`, &rows, experimentID, metricName, startTime) @@ -412,7 +357,6 @@ SELECT t.id FROM ( FROM trials t JOIN validations v ON t.id = v.trial_id WHERE t.experiment_id=$2 - AND v.state = 'COMPLETED' GROUP BY t.id ORDER BY best_metric %s LIMIT $3 @@ -420,32 +364,6 @@ SELECT t.id FROM ( return trials, err } -// ExpCompareTopTrialsByMetric chooses the subset of trials from a list of experiments -// that recorded the best values for the specified metric at any point during the trial. -func (db *PgDB) ExpCompareTopTrialsByMetric(experimentIDs []int32, maxTrials int, metric string, - smallerIsBetter bool, -) (trials []int32, err error) { - order := desc - aggregate := max - if smallerIsBetter { - order = asc - aggregate = min - } - err = db.sql.Select(&trials, fmt.Sprintf(` -SELECT t.id FROM ( - SELECT t.id, - %s((v.metrics->'validation_metrics'->>$1)::float8) as best_metric - FROM trials t - JOIN validations v ON t.id = v.trial_id - WHERE t.experiment_id in (SELECT unnest($2::int [])::int) - AND v.state = 'COMPLETED' - GROUP BY t.id - ORDER BY best_metric %s - LIMIT $3 -) t;`, aggregate, order), metric, experimentIDs, maxTrials) - return trials, err -} - // TopTrialsByTrainingLength chooses the subset of trials that has been training for the highest // number of batches, using the specified metric as a tie breaker. func (db *PgDB) TopTrialsByTrainingLength(experimentID int, maxTrials int, metric string, @@ -466,7 +384,6 @@ SELECT t.id FROM ( FROM trials t JOIN validations v ON t.id = v.trial_id WHERE t.experiment_id=$2 - AND v.state = 'COMPLETED' GROUP BY t.id ORDER BY progress DESC, best_metric %s LIMIT $3 @@ -552,10 +469,8 @@ SELECT total_batches AS batches, s.end_time as end_time, s.metrics->>'avg_metrics' AS metrics -FROM trials t - INNER JOIN steps s ON t.id=s.trial_id -WHERE t.id=$2 - AND s.state = 'COMPLETED' +FROM steps s +WHERE s.trial_id=$2 AND total_batches >= $3 AND ($4 <= 0 OR total_batches <= $4) AND s.end_time > $5 @@ -581,10 +496,8 @@ SELECT v.total_batches AS batches, v.end_time as end_time, v.metrics->>'validation_metrics' AS metrics -FROM trials t -JOIN validations v ON t.id = v.trial_id -WHERE t.id=$2 - AND v.state = 'COMPLETED' +FROM validations v +WHERE v.trial_id=$2 AND v.total_batches >= $3 AND ($4 <= 0 OR v.total_batches <= $4) AND v.end_time > $5 @@ -641,7 +554,6 @@ FROM trials t FROM trials t INNER JOIN steps s ON t.id=s.trial_id WHERE t.experiment_id=$2 - AND s.state = 'COMPLETED' GROUP BY t.id, s.total_batches ) filter ON s.total_batches = filter.total_batches @@ -683,7 +595,6 @@ JOIN ( FROM trials t JOIN validations v ON t.id = v.trial_id WHERE t.experiment_id=$2 - AND v.state = 'COMPLETED' GROUP BY t.id, v.total_batches ) filter ON v.total_batches = filter.total_batches @@ -809,7 +720,6 @@ SELECT (v.metrics->'validation_metrics'->>$2)::float8 FROM validations v, trials t WHERE v.trial_id = t.id AND t.experiment_id = $1 - AND v.state = 'COMPLETED' ORDER BY (v.metrics->'validation_metrics'->>$2)::float8 %s LIMIT 1`, metricOrdering), id, exp.Config.Searcher.Metric).Scan(&metric); { case errors.Is(err, sql.ErrNoRows): @@ -1417,12 +1327,12 @@ WITH const AS ( c.report_time as end_time, c.uuid, c.resources, c.metadata, (SELECT row_to_json(s) FROM ( - SELECT s.end_time, s.id, s.state, s.trial_id, + SELECT s.end_time, s.id, s.trial_id, s.total_batches, (SELECT row_to_json(v) FROM ( SELECT v.end_time, v.id, v.metrics, - v.state, v.total_batches, v.trial_id + v.total_batches, v.trial_id FROM validations v WHERE v.trial_id = t.id AND v.total_batches = s.total_batches ) v diff --git a/master/internal/db/postgres_trial.go b/master/internal/db/postgres_trial.go index 177aa8ebc6d..8254a3647a2 100644 --- a/master/internal/db/postgres_trial.go +++ b/master/internal/db/postgres_trial.go @@ -182,15 +182,12 @@ WHERE trial_id = $1 if _, err := tx.NamedExecContext(ctx, ` INSERT INTO raw_steps - (trial_id, trial_run_id, state, - end_time, metrics, total_batches) + (trial_id, trial_run_id, end_time, metrics, total_batches) VALUES - (:trial_id, :trial_run_id, :state, - now(), :metrics, :total_batches) + (:trial_id, :trial_run_id, now(), :metrics, :total_batches) `, model.TrialMetrics{ TrialID: int(m.TrialId), TrialRunID: int(m.TrialRunId), - State: model.CompletedState, Metrics: map[string]interface{}{ "avg_metrics": m.Metrics.AvgMetrics, "batch_metrics": m.Metrics.BatchMetrics, @@ -231,15 +228,12 @@ WHERE trial_id = $1 if _, err := tx.NamedExecContext(ctx, ` INSERT INTO raw_validations - (trial_id, trial_run_id, state, end_time, - metrics, total_batches) + (trial_id, trial_run_id, end_time, metrics, total_batches) VALUES - (:trial_id, :trial_run_id, :state, now(), - :metrics, :total_batches) + (:trial_id, :trial_run_id, now(), :metrics, :total_batches) `, model.TrialMetrics{ TrialID: int(m.TrialId), TrialRunID: int(m.TrialRunId), - State: model.CompletedState, Metrics: map[string]interface{}{ "validation_metrics": m.Metrics.AvgMetrics, }, @@ -277,17 +271,16 @@ SELECT EXISTS(SELECT 1 FROM steps WHERE trial_id = $1 AND total_batches = $2);`, if _, err := tx.NamedExecContext(ctx, ` INSERT INTO raw_steps - (trial_id, trial_run_id, state, + (trial_id, trial_run_id, end_time, metrics, total_batches) VALUES - (:trial_id, :trial_run_id, :state, + (:trial_id, :trial_run_id, :end_time, :metrics, :total_batches) ON CONFLICT (trial_id, trial_run_id, total_batches) DO NOTHING `, model.TrialMetrics{ TrialID: trialID, TrialRunID: trialRunID, - State: model.CompletedState, EndTime: ptrs.Ptr(time.Now().UTC()), Metrics: map[string]interface{}{ "avg_metrics": struct{}{}, @@ -342,7 +335,7 @@ WHERE id = $1 func (db *PgDB) ValidationByTotalBatches(trialID, totalBatches int) (*model.TrialMetrics, error) { var validation model.TrialMetrics if err := db.query(` -SELECT id, trial_id, total_batches, state, end_time, metrics +SELECT id, trial_id, total_batches, end_time, metrics FROM validations WHERE trial_id = $1 AND total_batches = $2`, &validation, trialID, totalBatches); errors.Cause(err) == ErrNotFound { @@ -437,7 +430,7 @@ WITH const AS ( FROM ( SELECT * FROM validations where id = (select best_validation_id from trials where id = $1) UNION ALL - SELECT * FROM validations + SELECT * FROM validations where trial_id = $1 and trial_run_id = $2 and total_batches = $3 diff --git a/master/internal/mocks/db.go b/master/internal/mocks/db.go index 1100aed8527..652f707a0d3 100644 --- a/master/internal/mocks/db.go +++ b/master/internal/mocks/db.go @@ -562,81 +562,6 @@ func (_m *DB) EndInstanceStats(a *model.InstanceStats) error { return r0 } -// ExpCompareMetricNames provides a mock function with given fields: trialIDs, sStartTime, vStartTime -func (_m *DB) ExpCompareMetricNames(trialIDs []int32, sStartTime time.Time, vStartTime time.Time) ([]string, []string, time.Time, time.Time, error) { - ret := _m.Called(trialIDs, sStartTime, vStartTime) - - var r0 []string - var r1 []string - var r2 time.Time - var r3 time.Time - var r4 error - if rf, ok := ret.Get(0).(func([]int32, time.Time, time.Time) ([]string, []string, time.Time, time.Time, error)); ok { - return rf(trialIDs, sStartTime, vStartTime) - } - if rf, ok := ret.Get(0).(func([]int32, time.Time, time.Time) []string); ok { - r0 = rf(trialIDs, sStartTime, vStartTime) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func([]int32, time.Time, time.Time) []string); ok { - r1 = rf(trialIDs, sStartTime, vStartTime) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]string) - } - } - - if rf, ok := ret.Get(2).(func([]int32, time.Time, time.Time) time.Time); ok { - r2 = rf(trialIDs, sStartTime, vStartTime) - } else { - r2 = ret.Get(2).(time.Time) - } - - if rf, ok := ret.Get(3).(func([]int32, time.Time, time.Time) time.Time); ok { - r3 = rf(trialIDs, sStartTime, vStartTime) - } else { - r3 = ret.Get(3).(time.Time) - } - - if rf, ok := ret.Get(4).(func([]int32, time.Time, time.Time) error); ok { - r4 = rf(trialIDs, sStartTime, vStartTime) - } else { - r4 = ret.Error(4) - } - - return r0, r1, r2, r3, r4 -} - -// ExpCompareTopTrialsByMetric provides a mock function with given fields: experimentID, maxTrials, metric, smallerIsBetter -func (_m *DB) ExpCompareTopTrialsByMetric(experimentID []int32, maxTrials int, metric string, smallerIsBetter bool) ([]int32, error) { - ret := _m.Called(experimentID, maxTrials, metric, smallerIsBetter) - - var r0 []int32 - var r1 error - if rf, ok := ret.Get(0).(func([]int32, int, string, bool) ([]int32, error)); ok { - return rf(experimentID, maxTrials, metric, smallerIsBetter) - } - if rf, ok := ret.Get(0).(func([]int32, int, string, bool) []int32); ok { - r0 = rf(experimentID, maxTrials, metric, smallerIsBetter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int32) - } - } - - if rf, ok := ret.Get(1).(func([]int32, int, string, bool) error); ok { - r1 = rf(experimentID, maxTrials, metric, smallerIsBetter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ExperimentBestSearcherValidation provides a mock function with given fields: id func (_m *DB) ExperimentBestSearcherValidation(id int) (float32, error) { ret := _m.Called(id) diff --git a/master/internal/rm/kubernetesrm/pods.go b/master/internal/rm/kubernetesrm/pods.go index a55da9d2c1d..322081c7001 100644 --- a/master/internal/rm/kubernetesrm/pods.go +++ b/master/internal/rm/kubernetesrm/pods.go @@ -933,14 +933,22 @@ func (p *pods) summarize(ctx *actor.Context) (map[string]model.AgentSummary, err // Look up quotas for our resource pools' namespaces. for namespace := range p.namespaceToPoolName { quotaList, err := p.quotaInterfaces[namespace].List(context.TODO(), metaV1.ListOptions{}) - if k8serrors.IsNotFound(err) || quotaList == nil || len(quotaList.Items) != 1 { + if err != nil && !k8serrors.IsNotFound(err) { + return nil, err + } else if k8serrors.IsNotFound(err) || quotaList == nil { + continue + } + + relevantQuotas := cpuAndGpuQuotas(quotaList) + if len(relevantQuotas) != 1 { // TODO: figure out how we want to handle multiple quotas per namespace? + // When there's multiple conflicting quotas, k8s seems to use the most + // restrictive of them—i.e. if there's a quota limiting to 100 CPUs and one + // limiting to 10, only 10 CPUs will be allowed. continue - } else if err != nil { - return nil, err } - namespaceToQuota[namespace] = quotaList.Items[0] + namespaceToQuota[namespace] = relevantQuotas[0] } // If there's only one resource pool configured and it doesn't have a quota, summarize using the @@ -972,7 +980,7 @@ func (p *pods) summarize(ctx *actor.Context) (map[string]model.AgentSummary, err switch resourceName { case k8sV1.ResourceCPU: deviceType = device.CPU - case ResourceTypeNvidia: + case ResourceTypeNvidia, "limits." + ResourceTypeNvidia: deviceType = device.CUDA default: // We only care about CPU and GPU quotas for the slots summary @@ -1202,3 +1210,21 @@ func numSlots(slots model.SlotsSummary) int { return slotCountsByType[device.CPU] } + +func cpuAndGpuQuotas(quotas *k8sV1.ResourceQuotaList) []k8sV1.ResourceQuota { + if quotas == nil || len(quotas.Items) == 0 { + return nil + } + + result := []k8sV1.ResourceQuota{} + for _, q := range quotas.Items { + for resourceName := range q.Spec.Hard { + switch resourceName { + case k8sV1.ResourceCPU, ResourceTypeNvidia, "limits." + ResourceTypeNvidia: + result = append(result, q) + } + } + } + + return result +} diff --git a/master/internal/rm/kubernetesrm/resource_pool.go b/master/internal/rm/kubernetesrm/resource_pool.go index 199da379ad2..a59a02c995c 100644 --- a/master/internal/rm/kubernetesrm/resource_pool.go +++ b/master/internal/rm/kubernetesrm/resource_pool.go @@ -651,7 +651,7 @@ func (p k8sPodResources) Summary() sproto.ResourcesSummary { ResourcesType: sproto.ResourcesTypeK8sPod, AgentDevices: map[aproto.ID][]device.Device{ // TODO: Make it more obvious k8s can't be trusted. - aproto.ID(p.podsActor.Address().Local()): nil, + aproto.ID(p.podsActor.Address().Local()): make([]device.Device, p.slots), }, ContainerID: &p.containerID, diff --git a/master/internal/rm/tasklist/task_list.go b/master/internal/rm/tasklist/task_list.go index 621b0aad386..2aa7b2b1cdb 100644 --- a/master/internal/rm/tasklist/task_list.go +++ b/master/internal/rm/tasklist/task_list.go @@ -195,6 +195,7 @@ func newTaskSummary( SlotsNeeded: request.SlotsNeeded, Resources: resourcesSummaries, SchedulerType: schedulerType, + ProxyPorts: request.ProxyPorts, } if group, ok := groups[request.Group]; ok { diff --git a/master/internal/sproto/resources.go b/master/internal/sproto/resources.go index 587363341cb..dacc4d7160b 100644 --- a/master/internal/sproto/resources.go +++ b/master/internal/sproto/resources.go @@ -155,7 +155,10 @@ func NewResourcesFailure( func (f ResourcesFailure) Error() string { if f.ExitCode == nil { - return fmt.Sprintf("%s: %s", f.FailureType, f.ErrMsg) + if len(f.ErrMsg) > 0 { + return fmt.Sprintf("%s: %s", f.FailureType, f.ErrMsg) + } + return fmt.Sprintf("%s", f.FailureType) } return fmt.Sprintf("%s: %s (exit code %d)", f.FailureType, f.ErrMsg, *f.ExitCode) } diff --git a/master/internal/sproto/task.go b/master/internal/sproto/task.go index 46612bddaca..62205c92f23 100644 --- a/master/internal/sproto/task.go +++ b/master/internal/sproto/task.go @@ -2,6 +2,7 @@ package sproto import ( "fmt" + "strconv" "time" "golang.org/x/exp/maps" @@ -13,6 +14,7 @@ import ( "github.com/determined-ai/determined/master/pkg/logger" "github.com/determined-ai/determined/master/pkg/model" "github.com/determined-ai/determined/master/pkg/ptrs" + "github.com/determined-ai/determined/master/pkg/schemas/expconf" "github.com/determined-ai/determined/master/pkg/tasks" ) @@ -42,7 +44,7 @@ type ( // Behavioral configuration. Preemptible bool IdleTimeout *IdleTimeoutConfig - ProxyPort *ProxyPortConfig + ProxyPorts []*ProxyPortConfig StreamEvents *EventStreamConfig Restore bool @@ -61,10 +63,10 @@ type ( // ProxyPortConfig configures a proxy the allocation should start. ProxyPortConfig struct { - ServiceID string - Port int - ProxyTCP bool - Unauthenticated bool + ServiceID string `json:"service_id"` + Port int `json:"port"` + ProxyTCP bool `json:"proxy_tcp"` + Unauthenticated bool `json:"unauthenticated"` } // EventStreamConfig configures an event stream. @@ -94,6 +96,7 @@ type ( Resources []ResourcesSummary `json:"resources"` SchedulerType string `json:"scheduler_type"` Priority *int `json:"priority"` + ProxyPorts []*ProxyPortConfig `json:"proxy_ports,omitempty"` } // SetAllocationName sets the name of the task. SetAllocationName struct { @@ -311,3 +314,22 @@ func (ev *Event) ToTaskLog() model.TaskLog { // ResourceList is a wrapper for a list of resources. type ResourceList map[ResourcesID]Resources + +// NewProxyPortConfig converts expconf proxy configs into internal representation. +func NewProxyPortConfig(input expconf.ProxyPortsConfig, taskID model.TaskID) []*ProxyPortConfig { + out := []*ProxyPortConfig{} + for _, epp := range input { + serviceID := string(taskID) + if !epp.DefaultServiceID() { + serviceID = string(taskID) + ":" + strconv.Itoa(epp.ProxyPort()) + } + out = append(out, &ProxyPortConfig{ + Port: epp.ProxyPort(), + ProxyTCP: epp.ProxyTCP(), + Unauthenticated: epp.Unauthenticated(), + ServiceID: serviceID, + }) + } + + return out +} diff --git a/master/internal/task/allocation.go b/master/internal/task/allocation.go index 1f371d32ada..90796ade367 100644 --- a/master/internal/task/allocation.go +++ b/master/internal/task/allocation.go @@ -249,7 +249,7 @@ func (a *Allocation) Receive(ctx *actor.Context) error { ctx.Respond(a.State()) } case SetAllocationProxyAddress: - if a.req.ProxyPort == nil { + if len(a.req.ProxyPorts) == 0 { if ctx.ExpectingResponse() { ctx.Respond(ErrBehaviorUnsupported{Behavior: fmt.Sprintf("%T", msg)}) } @@ -290,7 +290,7 @@ func (a *Allocation) Receive(ctx *actor.Context) error { a.rendezvous.unwatch(msg) case rendezvousTimeout: if err := a.rendezvous.checkTimeout(msg); err != nil { - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{Log: err.Error()})) + a.sendTaskLog(ctx, model.TaskLog{Log: err.Error()}) } default: a.Error(ctx, actor.ErrUnexpectedMessage(ctx)) @@ -314,7 +314,7 @@ func (a *Allocation) Receive(ctx *actor.Context) error { a.allGather.unwatch(msg) case allGatherTimeout: if err := a.allGather.checkTimeout(msg); err != nil { - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{Log: err.Error()})) + a.sendTaskLog(ctx, model.TaskLog{Log: err.Error()}) ctx.Log().WithError(err).Error("performing all gather through master") } default: @@ -332,7 +332,7 @@ func (a *Allocation) Receive(ctx *actor.Context) error { return nil } if err := a.preemption.ReceiveMsg(ctx); err != nil { - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{Log: err.Error()})) + a.sendTaskLog(ctx, model.TaskLog{Log: err.Error()}) a.Error(ctx, err) } case IdleTimeoutWatcherTick, IdleWatcherNoteActivity: @@ -479,9 +479,11 @@ func (a *Allocation) ResourcesAllocated(ctx *actor.Context, msg sproto.Resources } } else if a.getModelState() == model.AllocationStateRunning { // Restore proxies. - for _, r := range a.resources { - if a.req.ProxyPort != nil && r.Started != nil && r.Started.Addresses != nil { - a.registerProxies(ctx, r.Started.Addresses) + if len(a.req.ProxyPorts) > 0 { + for _, r := range a.resources { + if r.Rank == 0 && r.Started != nil && r.Started.Addresses != nil { + a.registerProxies(ctx, r.Started.Addresses) + } } } } @@ -504,12 +506,12 @@ func (a *Allocation) SetResourcesAsDaemon( ctx.Respond(ErrStaleResources{ID: rID}) return nil } else if len(a.resources) <= 1 { - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{ + a.sendTaskLog(ctx, model.TaskLog{ Log: `Ignoring request to daemonize resources within an allocation for an allocation with only one manageable set of resources, because this would just kill it. This is expected in when using the HPC launcher.`, Level: ptrs.Ptr(model.LogLevelInfo), - })) + }) return nil } @@ -581,7 +583,8 @@ func (a *Allocation) ResourcesStateChanged( ctx.Log(). Info("all containers are connected successfully (task container state changed)") } - if a.req.ProxyPort != nil && msg.ResourcesStarted.Addresses != nil { + if len(a.req.ProxyPorts) > 0 && msg.ResourcesStarted.Addresses != nil && + a.resources[msg.ResourcesID].Rank == 0 { a.registerProxies(ctx, msg.ResourcesStarted.Addresses) } @@ -621,13 +624,13 @@ func (a *Allocation) ResourcesStateChanged( switch { case a.killedWhileRunning: - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{ + a.sendTaskLog(ctx, model.TaskLog{ ContainerID: msg.ContainerIDStr(), Log: fmt.Sprintf( "resources were killed: %s", msg.ResourcesStopped.String(), ), - })) + }) a.Exit(ctx, "resources were killed") case msg.ResourcesStopped.Failure != nil: // Avoid erroring out if we have killed our daemons gracefully. @@ -640,11 +643,11 @@ func (a *Allocation) ResourcesStateChanged( a.Error(ctx, *msg.ResourcesStopped.Failure) } default: - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{ + a.sendTaskLog(ctx, model.TaskLog{ ContainerID: msg.ContainerIDStr(), Log: msg.ResourcesStopped.String(), Level: ptrs.Ptr(model.LogLevelInfo), - })) + }) a.Exit(ctx, msg.ResourcesStopped.String()) } @@ -768,13 +771,13 @@ func (a *Allocation) exitedWithoutErr() bool { func (a *Allocation) preempt(ctx *actor.Context, reason string) { ctx.Log().WithField("reason", reason).Info("decided to gracefully terminate allocation") - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{ + a.sendTaskLog(ctx, model.TaskLog{ Level: ptrs.Ptr(model.LogLevelInfo), Log: fmt.Sprintf( "gracefully terminating allocation's remaining resources (reason: %s)", reason, ), - })) + }) a.preemption.Preempt() actors.NotifyAfter(ctx, preemptionTimeoutDuration, PreemptionTimeout{a.model.AllocationID}) @@ -787,13 +790,13 @@ func (a *Allocation) kill(ctx *actor.Context, reason string) { } ctx.Log().WithField("reason", reason).Info("decided to kill allocation") - a.logger.Insert(ctx, a.enrichLog(model.TaskLog{ + a.sendTaskLog(ctx, model.TaskLog{ Level: ptrs.Ptr(model.LogLevelInfo), Log: fmt.Sprintf( "forcibly killing allocation's remaining resources (reason: %s)", reason, ), - })) + }) for _, r := range a.resources.active() { r.Kill(ctx, a.logCtx) @@ -813,13 +816,8 @@ func (a *Allocation) kill(ctx *actor.Context, reason string) { } func (a *Allocation) registerProxies(ctx *actor.Context, addresses []cproto.Address) { - cfg := a.req.ProxyPort - if cfg == nil { - return - } - if len(a.resources) > 1 { - // We don't support proxying multi-reservation allocations. - ctx.Log().Warnf("proxy for multi-reservation allocation aborted") + // For multi-reservation allocations, proxies are only setup for rank=0 (i.e. the chief). + if len(a.req.ProxyPorts) == 0 { return } @@ -828,7 +826,13 @@ func (a *Allocation) registerProxies(ctx *actor.Context, addresses []cproto.Addr // additional addresses will appear her, but currently we only proxy one uuid to one // port, so it doesn't make sense to send multiple proxy.Register messages for a // single ServiceID (only the last one would work). - if address.ContainerPort != cfg.Port { + var pcfg *sproto.ProxyPortConfig + for _, cfg := range a.req.ProxyPorts { + if address.ContainerPort == cfg.Port { + pcfg = cfg + } + } + if pcfg == nil { continue } @@ -836,25 +840,29 @@ func (a *Allocation) registerProxies(ctx *actor.Context, addresses []cproto.Addr // proxy multi-container tasks or when containers are created prior to being // assigned to an agent. ctx.Ask(ctx.Self().System().Get(actor.Addr("proxy")), proxy.Register{ - ServiceID: cfg.ServiceID, + ServiceID: pcfg.ServiceID, URL: &url.URL{ Scheme: "http", Host: fmt.Sprintf("%s:%d", address.HostIP, address.HostPort), }, - ProxyTCP: cfg.ProxyTCP, - Unauthenticated: cfg.Unauthenticated, + ProxyTCP: pcfg.ProxyTCP, + Unauthenticated: pcfg.Unauthenticated, }) - a.proxies = append(a.proxies, cfg.ServiceID) + ctx.Log().Debugf("registered proxy id: %s, tcp: %v\n", pcfg.ServiceID, pcfg.ProxyTCP) + a.proxies = append(a.proxies, pcfg.ServiceID) } - if len(a.proxies) != 1 { - ctx.Log().Errorf("did not proxy as expected %v (found addrs %v)", len(a.proxies), addresses) + if len(a.proxies) != len(a.req.ProxyPorts) { + a.sendTaskLog(ctx, model.TaskLog{ + Log: fmt.Sprintf( + "did not proxy as expected %v (found addrs %v, requested %v)", + len(a.proxies), addresses, len(a.req.ProxyPorts)), + }) } } func (a *Allocation) unregisterProxies(ctx *actor.Context) { - cfg := a.req.ProxyPort - if cfg == nil { + if len(a.req.ProxyPorts) == 0 { return } @@ -872,17 +880,22 @@ func (a *Allocation) unregisterProxies(ctx *actor.Context) { // containerProxyAddresses forms the container address when proxyAddress is given. func (a *Allocation) containerProxyAddresses() []cproto.Address { - if a.proxyAddress == nil || a.req.ProxyPort == nil { + if a.proxyAddress == nil || len(a.req.ProxyPorts) == 0 { return []cproto.Address{} } - return []cproto.Address{ - { + + result := []cproto.Address{} + + for _, pp := range a.req.ProxyPorts { + result = append(result, cproto.Address{ ContainerIP: *a.proxyAddress, - ContainerPort: a.req.ProxyPort.Port, + ContainerPort: pp.Port, HostIP: *a.proxyAddress, - HostPort: a.req.ProxyPort.Port, - }, + HostPort: pp.Port, + }) } + + return result } func (a *Allocation) terminated(ctx *actor.Context, reason string) { @@ -1034,9 +1047,13 @@ func (a *Allocation) enrichLog(log model.TaskLog) model.TaskLog { return log } +func (a *Allocation) sendTaskLog(ctx *actor.Context, log model.TaskLog) { + a.logger.Insert(ctx, a.enrichLog(log)) +} + func (a *Allocation) sendEvent(ctx *actor.Context, ev sproto.Event) { ev = a.enrichEvent(ctx, ev) - a.logger.Insert(ctx, a.enrichLog(ev.ToTaskLog())) + a.sendTaskLog(ctx, ev.ToTaskLog()) if a.req.StreamEvents != nil { ctx.Tell(a.req.StreamEvents.To, ev) } diff --git a/master/internal/trial.go b/master/internal/trial.go index 106d9cd77c0..4c546c44b65 100644 --- a/master/internal/trial.go +++ b/master/internal/trial.go @@ -295,6 +295,8 @@ func (t *trial) maybeAllocateTask(ctx *actor.Context) error { Preemptible: true, Restore: true, + ProxyPorts: sproto.NewProxyPortConfig( + tasks.TrialSpecProxyPorts(t.taskSpec, t.config), t.taskID), } ctx.Log(). WithField("allocation-id", ar.AllocationID). @@ -328,6 +330,7 @@ func (t *trial) maybeAllocateTask(ctx *actor.Context) error { }, Preemptible: true, + ProxyPorts: sproto.NewProxyPortConfig(tasks.TrialSpecProxyPorts(t.taskSpec, t.config), t.taskID), } ctx.Log(). diff --git a/master/pkg/model/compat.go b/master/pkg/model/compat.go index c10d3ab50a0..b7ecb4d6a0c 100644 --- a/master/pkg/model/compat.go +++ b/master/pkg/model/compat.go @@ -91,14 +91,35 @@ func (r RuntimeItem) ToExpconf() expconf.EnvironmentImageMap { }) } +// ToExpconf translates old model objects into an expconf object. +func (p ProxyPort) ToExpconf() expconf.ProxyPort { + return schemas.WithDefaults(expconf.ProxyPort{ + RawProxyPort: p.ProxyPort, + RawProxyTCP: &p.ProxyTCP, + RawUnauthenticated: &p.Unauthenticated, + RawDefaultServiceID: &p.DefaultServiceID, + }) +} + +// ToExpconf translates old model objects into an expconf object. +func (p ProxyPortsConfig) ToExpconf() expconf.ProxyPortsConfig { + var out expconf.ProxyPortsConfig + for _, pp := range p { + out = append(out, pp.ToExpconf()) + } + return schemas.WithDefaults(out) +} + // ToExpconf translates old model objects into an expconf object. func (e Environment) ToExpconf() expconf.EnvironmentConfig { image := e.Image.ToExpconf() vars := e.EnvironmentVariables.ToExpconf() + proxyConf := e.ProxyPorts.ToExpconf() return schemas.WithDefaults(expconf.EnvironmentConfig{ RawImage: &image, RawEnvironmentVariables: &vars, + RawProxyPorts: &proxyConf, RawPorts: e.Ports, RawRegistryAuth: e.RegistryAuth, RawForcePullImage: ptrs.Ptr(e.ForcePullImage), diff --git a/master/pkg/model/environment_config.go b/master/pkg/model/environment_config.go index 1f5404d0ca3..4d70618fa3c 100644 --- a/master/pkg/model/environment_config.go +++ b/master/pkg/model/environment_config.go @@ -28,8 +28,9 @@ const ( // Environment configures the environment of a Determined command or experiment. type Environment struct { - Image RuntimeItem `json:"image"` - EnvironmentVariables RuntimeItems `json:"environment_variables,omitempty"` + Image RuntimeItem `json:"image"` + EnvironmentVariables RuntimeItems `json:"environment_variables,omitempty"` + ProxyPorts ProxyPortsConfig `json:"proxy_ports"` Ports map[string]int `json:"ports"` RegistryAuth *types.AuthConfig `json:"registry_auth,omitempty"` diff --git a/master/pkg/model/experiment.go b/master/pkg/model/experiment.go index c3c78d32a24..5ca655a8b2f 100644 --- a/master/pkg/model/experiment.go +++ b/master/pkg/model/experiment.go @@ -467,7 +467,6 @@ type TrialMetrics struct { TrialID int `db:"trial_id" json:"trial_id"` TrialRunID int `db:"trial_run_id" json:"-"` TotalBatches int `db:"total_batches" json:"total_batches"` - State State `db:"state" json:"state"` EndTime *time.Time `db:"end_time" json:"end_time"` Metrics JSONObj `db:"metrics" json:"metrics"` } diff --git a/master/pkg/model/experiment_config.go b/master/pkg/model/experiment_config.go index 5887ae94a42..7cfce82a9c3 100644 --- a/master/pkg/model/experiment_config.go +++ b/master/pkg/model/experiment_config.go @@ -218,3 +218,15 @@ func (b *BindMount) UnmarshalJSON(data []byte) error { type DefaultParser *BindMount return errors.Wrap(json.Unmarshal(data, DefaultParser(b)), "failed to parse bind mounts") } + +// ProxyPort is a legacy-style clone of expconf.ProxyPort. +// TODO(ilia): migrate command config to expconf. +type ProxyPort struct { + ProxyPort int `json:"proxy_port"` + ProxyTCP bool `json:"proxy_tcp"` + Unauthenticated bool `json:"unauthenticated"` + DefaultServiceID bool `json:"default_service_id"` +} + +// ProxyPortsConfig is a legacy-style clone of expconf.ProxyPortsConfig. +type ProxyPortsConfig []ProxyPort diff --git a/master/pkg/schemas/expconf/const.go b/master/pkg/schemas/expconf/const.go index 314916498c5..b8a5b90d501 100644 --- a/master/pkg/schemas/expconf/const.go +++ b/master/pkg/schemas/expconf/const.go @@ -8,7 +8,7 @@ const ( // Default task environment docker image names. const ( - CPUImage = "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c" - CUDAImage = "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c" - ROCMImage = "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c" + CPUImage = "determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364" + CUDAImage = "determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364" + ROCMImage = "determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364" ) diff --git a/master/pkg/schemas/expconf/environment_config.go b/master/pkg/schemas/expconf/environment_config.go index 7c84a5626fb..8f75b418a7c 100644 --- a/master/pkg/schemas/expconf/environment_config.go +++ b/master/pkg/schemas/expconf/environment_config.go @@ -46,6 +46,7 @@ func (p PodSpec) WithDefaults() PodSpec { type EnvironmentConfigV0 struct { RawImage *EnvironmentImageMapV0 `json:"image"` RawEnvironmentVariables *EnvironmentVariablesMapV0 `json:"environment_variables"` + RawProxyPorts *ProxyPortsConfigV0 `json:"proxy_ports"` RawPorts map[string]int `json:"ports"` RawRegistryAuth *types.AuthConfig `json:"registry_auth"` @@ -219,3 +220,36 @@ func (e EnvironmentVariablesMapV0) For(deviceType device.Type) []string { panic(fmt.Sprintf("unexpected device type: %s", deviceType)) } } + +// ProxyPortV0 configures the master-proxied task ports. +// +//go:generate ../gen.sh +type ProxyPortV0 struct { + RawProxyPort int `json:"proxy_port"` + RawProxyTCP *bool `json:"proxy_tcp"` + RawUnauthenticated *bool `json:"unauthenticated"` + RawDefaultServiceID *bool `json:"default_service_id"` +} + +// ProxyPortsConfigV0 is the configuration for proxy ports. +// +//go:generate ../gen.sh +type ProxyPortsConfigV0 []ProxyPortV0 + +// Merge implemenets the mergable interface. +func (b ProxyPortsConfigV0) Merge(other ProxyPortsConfigV0) ProxyPortsConfigV0 { + out := ProxyPortsConfigV0{} + out = append(out, b...) + + // Prevent duplicate container ports as a result of the merge. + ports := map[int]bool{} + for _, pp := range b { + ports[pp.ProxyPort()] = true + } + for _, pp := range other { + if _, ok := ports[pp.ProxyPort()]; !ok { + out = append(out, pp) + } + } + return out +} diff --git a/master/pkg/schemas/expconf/latest.go b/master/pkg/schemas/expconf/latest.go index 617b616a5de..ae2ceaab698 100644 --- a/master/pkg/schemas/expconf/latest.go +++ b/master/pkg/schemas/expconf/latest.go @@ -40,6 +40,8 @@ type ( SingleConfig = SingleConfigV0 SlurmConfig = SlurmConfigV0 PbsConfig = PbsConfigV0 + ProxyPort = ProxyPortV0 + ProxyPortsConfig = ProxyPortsConfigV0 ) // These are EOL searchers, not to be used in new experiments. diff --git a/master/pkg/schemas/expconf/legacy_test.go b/master/pkg/schemas/expconf/legacy_test.go index 285e592009a..ebe6980e65a 100644 --- a/master/pkg/schemas/expconf/legacy_test.go +++ b/master/pkg/schemas/expconf/legacy_test.go @@ -111,9 +111,10 @@ func TestLegacyConfig(t *testing.T) { RawImage: &EnvironmentImageMap{ RawCPU: ptrs.Ptr("determinedai/environments:py-3.6.9-pytorch-1.4-tf-1.15-cpu-aaa3750"), RawCUDA: ptrs.Ptr("determinedai/environments:cuda-10.0-pytorch-1.4-tf-1.15-gpu-aaa3750"), - RawROCM: ptrs.Ptr("determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c"), + RawROCM: ptrs.Ptr("determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364"), }, RawPorts: map[string]int{}, + RawProxyPorts: &ProxyPortsConfigV0{}, RawForcePullImage: ptrs.Ptr(false), RawAddCapabilities: []string{}, RawDropCapabilities: []string{}, @@ -244,7 +245,7 @@ func TestLegacyConfig(t *testing.T) { RawImage: &EnvironmentImageMap{ RawCPU: ptrs.Ptr("determinedai/environments:py-3.6.9-pytorch-1.4-tf-1.15-cpu-067db2b"), RawCUDA: ptrs.Ptr("determinedai/environments:cuda-10.0-pytorch-1.4-tf-1.15-gpu-067db2b"), - RawROCM: ptrs.Ptr("determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c"), + RawROCM: ptrs.Ptr("determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364"), }, RawPodSpec: &PodSpec{ TypeMeta: metaV1.TypeMeta{ @@ -280,6 +281,7 @@ func TestLegacyConfig(t *testing.T) { Status: k8sV1.PodStatus{}, }, RawPorts: map[string]int{}, + RawProxyPorts: &ProxyPortsConfigV0{}, RawForcePullImage: ptrs.Ptr(false), RawAddCapabilities: []string{}, RawDropCapabilities: []string{}, @@ -330,8 +332,8 @@ func TestLegacyConfig(t *testing.T) { gpu: [] force_pull_image: false image: - cpu: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-ad0591c - gpu: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-ad0591c + cpu: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-7aa5364 + gpu: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-7aa5364 ports: {} registry_auth: null hyperparameters: @@ -408,11 +410,12 @@ func TestLegacyConfig(t *testing.T) { RawROCM: []string{}, }, RawImage: &EnvironmentImageMap{ - RawCPU: ptrs.Ptr("determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-ad0591c"), - RawCUDA: ptrs.Ptr("determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-ad0591c"), - RawROCM: ptrs.Ptr("determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c"), + RawCPU: ptrs.Ptr("determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-7aa5364"), + RawCUDA: ptrs.Ptr("determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-7aa5364"), + RawROCM: ptrs.Ptr("determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364"), }, RawPorts: map[string]int{}, + RawProxyPorts: &ProxyPortsConfigV0{}, RawForcePullImage: ptrs.Ptr(false), RawAddCapabilities: []string{}, RawDropCapabilities: []string{}, diff --git a/master/pkg/schemas/expconf/zgen_environment_config_v0.go b/master/pkg/schemas/expconf/zgen_environment_config_v0.go index 490302d23fe..f5284335665 100644 --- a/master/pkg/schemas/expconf/zgen_environment_config_v0.go +++ b/master/pkg/schemas/expconf/zgen_environment_config_v0.go @@ -31,6 +31,17 @@ func (e *EnvironmentConfigV0) SetEnvironmentVariables(val EnvironmentVariablesMa e.RawEnvironmentVariables = &val } +func (e EnvironmentConfigV0) ProxyPorts() ProxyPortsConfigV0 { + if e.RawProxyPorts == nil { + panic("You must call WithDefaults on EnvironmentConfigV0 before .ProxyPorts") + } + return *e.RawProxyPorts +} + +func (e *EnvironmentConfigV0) SetProxyPorts(val ProxyPortsConfigV0) { + e.RawProxyPorts = &val +} + func (e EnvironmentConfigV0) Ports() map[string]int { return e.RawPorts } diff --git a/master/pkg/schemas/expconf/zgen_proxy_port_v0.go b/master/pkg/schemas/expconf/zgen_proxy_port_v0.go new file mode 100644 index 00000000000..e2747964c94 --- /dev/null +++ b/master/pkg/schemas/expconf/zgen_proxy_port_v0.go @@ -0,0 +1,62 @@ +// Code generated by gen.py. DO NOT EDIT. + +package expconf + +import ( + "github.com/santhosh-tekuri/jsonschema/v2" + + "github.com/determined-ai/determined/master/pkg/schemas" +) + +func (p ProxyPortV0) ProxyPort() int { + return p.RawProxyPort +} + +func (p *ProxyPortV0) SetProxyPort(val int) { + p.RawProxyPort = val +} + +func (p ProxyPortV0) ProxyTCP() bool { + if p.RawProxyTCP == nil { + panic("You must call WithDefaults on ProxyPortV0 before .ProxyTCP") + } + return *p.RawProxyTCP +} + +func (p *ProxyPortV0) SetProxyTCP(val bool) { + p.RawProxyTCP = &val +} + +func (p ProxyPortV0) Unauthenticated() bool { + if p.RawUnauthenticated == nil { + panic("You must call WithDefaults on ProxyPortV0 before .Unauthenticated") + } + return *p.RawUnauthenticated +} + +func (p *ProxyPortV0) SetUnauthenticated(val bool) { + p.RawUnauthenticated = &val +} + +func (p ProxyPortV0) DefaultServiceID() bool { + if p.RawDefaultServiceID == nil { + panic("You must call WithDefaults on ProxyPortV0 before .DefaultServiceID") + } + return *p.RawDefaultServiceID +} + +func (p *ProxyPortV0) SetDefaultServiceID(val bool) { + p.RawDefaultServiceID = &val +} + +func (p ProxyPortV0) ParsedSchema() interface{} { + return schemas.ParsedProxyPortV0() +} + +func (p ProxyPortV0) SanityValidator() *jsonschema.Schema { + return schemas.GetSanityValidator("http://determined.ai/schemas/expconf/v0/proxy-port.json") +} + +func (p ProxyPortV0) CompletenessValidator() *jsonschema.Schema { + return schemas.GetCompletenessValidator("http://determined.ai/schemas/expconf/v0/proxy-port.json") +} diff --git a/master/pkg/schemas/expconf/zgen_proxy_ports_config_v0.go b/master/pkg/schemas/expconf/zgen_proxy_ports_config_v0.go new file mode 100644 index 00000000000..5f8d315c0c1 --- /dev/null +++ b/master/pkg/schemas/expconf/zgen_proxy_ports_config_v0.go @@ -0,0 +1,21 @@ +// Code generated by gen.py. DO NOT EDIT. + +package expconf + +import ( + "github.com/santhosh-tekuri/jsonschema/v2" + + "github.com/determined-ai/determined/master/pkg/schemas" +) + +func (p ProxyPortsConfigV0) ParsedSchema() interface{} { + return schemas.ParsedProxyPortsConfigV0() +} + +func (p ProxyPortsConfigV0) SanityValidator() *jsonschema.Schema { + return schemas.GetSanityValidator("http://determined.ai/schemas/expconf/v0/proxy-ports.json") +} + +func (p ProxyPortsConfigV0) CompletenessValidator() *jsonschema.Schema { + return schemas.GetCompletenessValidator("http://determined.ai/schemas/expconf/v0/proxy-ports.json") +} diff --git a/master/pkg/schemas/zgen_schemas.go b/master/pkg/schemas/zgen_schemas.go index d92abad94d6..37ea48f4b43 100644 --- a/master/pkg/schemas/zgen_schemas.go +++ b/master/pkg/schemas/zgen_schemas.go @@ -587,6 +587,14 @@ var ( "default": [], "optionalRef": "http://determined.ai/schemas/expconf/v0/environment-variables.json" }, + "proxy_ports": { + "type": [ + "array", + "null" + ], + "default": [], + "optionalRef": "http://determined.ai/schemas/expconf/v0/proxy-ports.json" + }, "ports": { "type": [ "object", @@ -1618,6 +1626,53 @@ var ( "b": "end_after_batch" } } +`) + textProxyPortV0 = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://determined.ai/schemas/expconf/v0/proxy-port.json", + "title": "ProxyPort", + "additionalProperties": false, + "required": [ + "proxy_port" + ], + "type": "object", + "properties": { + "proxy_port": { + "type": "number" + }, + "proxy_tcp": { + "type": [ + "boolean", + "null" + ], + "default": false + }, + "unauthenticated": { + "type": [ + "boolean", + "null" + ], + "default": false + }, + "default_service_id": { + "type": [ + "boolean", + "null" + ], + "default": false + } + } +} +`) + textProxyPortsConfigV0 = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://determined.ai/schemas/expconf/v0/proxy-ports.json", + "title": "ProxyPortsConfig", + "type": "array", + "items": { + "$ref": "http://determined.ai/schemas/expconf/v0/proxy-port.json" + } +} `) textRegistryAuthV0 = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", @@ -3117,6 +3172,10 @@ var ( schemaProfilingConfigV0 interface{} + schemaProxyPortV0 interface{} + + schemaProxyPortsConfigV0 interface{} + schemaRegistryAuthV0 interface{} schemaReproducibilityConfigV0 interface{} @@ -3770,6 +3829,46 @@ func ParsedProfilingConfigV0() interface{} { return schemaProfilingConfigV0 } +func ParsedProxyPortV0() interface{} { + cacheLock.RLock() + if schemaProxyPortV0 != nil { + cacheLock.RUnlock() + return schemaProxyPortV0 + } + cacheLock.RUnlock() + + cacheLock.Lock() + defer cacheLock.Unlock() + if schemaProxyPortV0 != nil { + return schemaProxyPortV0 + } + err := json.Unmarshal(textProxyPortV0, &schemaProxyPortV0) + if err != nil { + panic("invalid embedded json for ProxyPortV0") + } + return schemaProxyPortV0 +} + +func ParsedProxyPortsConfigV0() interface{} { + cacheLock.RLock() + if schemaProxyPortsConfigV0 != nil { + cacheLock.RUnlock() + return schemaProxyPortsConfigV0 + } + cacheLock.RUnlock() + + cacheLock.Lock() + defer cacheLock.Unlock() + if schemaProxyPortsConfigV0 != nil { + return schemaProxyPortsConfigV0 + } + err := json.Unmarshal(textProxyPortsConfigV0, &schemaProxyPortsConfigV0) + if err != nil { + panic("invalid embedded json for ProxyPortsConfigV0") + } + return schemaProxyPortsConfigV0 +} + func ParsedRegistryAuthV0() interface{} { cacheLock.RLock() if schemaRegistryAuthV0 != nil { @@ -4305,6 +4404,10 @@ func schemaBytesMap() map[string][]byte { cachedSchemaBytesMap[url] = textOptimizationsConfigV0 url = "http://determined.ai/schemas/expconf/v0/profiling.json" cachedSchemaBytesMap[url] = textProfilingConfigV0 + url = "http://determined.ai/schemas/expconf/v0/proxy-port.json" + cachedSchemaBytesMap[url] = textProxyPortV0 + url = "http://determined.ai/schemas/expconf/v0/proxy-ports.json" + cachedSchemaBytesMap[url] = textProxyPortsConfigV0 url = "http://determined.ai/schemas/expconf/v0/registry-auth.json" cachedSchemaBytesMap[url] = textRegistryAuthV0 url = "http://determined.ai/schemas/expconf/v0/reproducibility.json" diff --git a/master/pkg/syncx/errgroupx/errgroupx.go b/master/pkg/syncx/errgroupx/errgroupx.go index 74078cc722f..e258ae3d498 100644 --- a/master/pkg/syncx/errgroupx/errgroupx.go +++ b/master/pkg/syncx/errgroupx/errgroupx.go @@ -9,15 +9,17 @@ import ( // Group is a thin wrapper around golang.org/x/sync/errgroup.Group that helps not leak its context // past the lifetime of the group. type Group struct { - inner errgroup.Group + inner *errgroup.Group ctx context.Context cancel context.CancelFunc } // WithContext creates a Group as a child of the given context. func WithContext(ctx context.Context) Group { - ctx, cancel := context.WithCancel(ctx) - return Group{ctx: ctx, cancel: cancel} + intermediateContext, cancel := context.WithCancel(ctx) + g, groupContext := errgroup.WithContext(intermediateContext) + + return Group{inner: g, ctx: groupContext, cancel: cancel} } // Go launch the given function in a goroutine as a member of the group. If the function returns an diff --git a/master/pkg/syncx/errgroupx/errgroupx_test.go b/master/pkg/syncx/errgroupx/errgroupx_test.go new file mode 100644 index 00000000000..384b469c37a --- /dev/null +++ b/master/pkg/syncx/errgroupx/errgroupx_test.go @@ -0,0 +1,53 @@ +package errgroupx + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestErrgroupxCancelByReturnError(t *testing.T) { + ctx := context.Background() + g := WithContext(ctx) + + finished := make(chan bool, 1) + g.Go(func(ctx context.Context) error { + return fmt.Errorf("non nil error") + }) + g.Go(func(ctx context.Context) error { + <-ctx.Done() + finished <- true + return nil + }) + + require.Error(t, g.Wait()) + require.True(t, <-finished) + require.NoError(t, ctx.Err()) // Original context not canceled. +} + +func TestErrgroupxCancelingParentCancels(t *testing.T) { + for _, cancelGroup := range []bool{true, false} { + ctx, cancel := context.WithCancel(context.Background()) + g := WithContext(ctx) + + finished := make(chan bool, 1) + g.Go(func(ctx context.Context) error { + <-ctx.Done() + finished <- true + return nil + }) + + if cancelGroup { + g.cancel() + require.NoError(t, ctx.Err()) + } else { + cancel() + require.Error(t, ctx.Err()) + } + require.True(t, <-finished) + require.NoError(t, g.Wait()) + cancel() + } +} diff --git a/master/pkg/tasks/task.go b/master/pkg/tasks/task.go index 6eb96e3ac07..09b6242523c 100644 --- a/master/pkg/tasks/task.go +++ b/master/pkg/tasks/task.go @@ -81,6 +81,12 @@ type TaskSpec struct { TaskType model.TaskType SlurmConfig expconf.SlurmConfig PbsConfig expconf.PbsConfig + + ExtraProxyPorts expconf.ProxyPortsConfig + + Workspace string + Project string + Labels []string } // ResolveWorkDir resolves the work dir. diff --git a/master/pkg/tasks/task_command.go b/master/pkg/tasks/task_command.go index 8cc167ca63d..bc51aec7b6a 100644 --- a/master/pkg/tasks/task_command.go +++ b/master/pkg/tasks/task_command.go @@ -3,11 +3,14 @@ package tasks import ( "archive/tar" "encoding/json" + "strconv" "github.com/determined-ai/determined/master/pkg/archive" "github.com/determined-ai/determined/master/pkg/cproto" "github.com/determined-ai/determined/master/pkg/etc" "github.com/determined-ai/determined/master/pkg/model" + "github.com/determined-ai/determined/master/pkg/schemas" + "github.com/determined-ai/determined/master/pkg/schemas/expconf" "github.com/determined-ai/determined/master/pkg/ssh" ) @@ -47,10 +50,6 @@ type GenericCommandSpec struct { Keys *ssh.PrivateAndPublicKeys - Port *int - ProxyTCP bool - Unauthenticated bool - WatchProxyIdleTimeout bool WatchRunnerIdleTimeout bool @@ -61,6 +60,7 @@ type GenericCommandSpec struct { func (s GenericCommandSpec) ToTaskSpec(keys *ssh.PrivateAndPublicKeys) TaskSpec { res := s.Base + s.MakeEnvPorts() res.Environment = s.Config.Environment.ToExpconf() res.ResourcesConfig = s.Config.Resources.ToExpconf() @@ -114,3 +114,38 @@ func (s GenericCommandSpec) ToTaskSpec(keys *ssh.PrivateAndPublicKeys) TaskSpec res.TaskType = s.TaskType return res } + +// TrialSpec has matching `ProxyPorts` and `MakeEnvPorts` methods. Long-term, we should +// - unify TrialSpec and GenericCommandSpec +// - move CommandConfig to expconf + +// MakeEnvPorts fills in `Environment.Ports` i.e. exposed ports for container config. +func (s *GenericCommandSpec) MakeEnvPorts() { + if s.Config.Environment.Ports != nil { + panic("CommandSpec Environment.Ports are only supposed to be generated once.") + } + + ppc := s.ProxyPorts() + s.Config.Environment.Ports = map[string]int{} + for _, pp := range ppc { + port := pp.ProxyPort() + s.Config.Environment.Ports[strconv.Itoa(port)] = port + } +} + +// ProxyPorts combines user-defined and system proxy configs. +func (s *GenericCommandSpec) ProxyPorts() expconf.ProxyPortsConfig { + env := schemas.WithDefaults(s.Config.Environment.ToExpconf()) + epp := schemas.WithDefaults(s.Base.ExtraProxyPorts) + out := make(expconf.ProxyPortsConfig, 0, len(epp)+len(env.ProxyPorts())) + + for _, pp := range epp { + out = append(out, pp) + } + + for _, pp := range env.ProxyPorts() { + out = append(out, pp) + } + + return out +} diff --git a/master/pkg/tasks/task_trial.go b/master/pkg/tasks/task_trial.go index 07141e698b2..27aa50d445c 100644 --- a/master/pkg/tasks/task_trial.go +++ b/master/pkg/tasks/task_trial.go @@ -11,6 +11,7 @@ import ( "github.com/determined-ai/determined/master/pkg/cproto" "github.com/determined-ai/determined/master/pkg/etc" "github.com/determined-ai/determined/master/pkg/model" + "github.com/determined-ai/determined/master/pkg/schemas" "github.com/determined-ai/determined/master/pkg/schemas/expconf" "github.com/determined-ai/determined/master/pkg/ssh" ) @@ -33,15 +34,7 @@ type TrialSpec struct { func (s TrialSpec) ToTaskSpec(keys *ssh.PrivateAndPublicKeys) TaskSpec { res := s.Base - env := s.ExperimentConfig.Environment() - ports := env.Ports() - if ports == nil { - ports = make(map[string]int) - } - // TODO: remove this, but without breaking rendezvous api. - ports["trial"] = 1734 - env.SetPorts(ports) - res.Environment = env + res.Environment = s.MakeEnvPorts() res.ResourcesConfig = s.ExperimentConfig.Resources() res.SlurmConfig = s.ExperimentConfig.SlurmConfig() @@ -154,3 +147,51 @@ func (s TrialSpec) ToTaskSpec(keys *ssh.PrivateAndPublicKeys) TaskSpec { return res } + +// MakeEnvPorts fills in `Environment.Ports` i.e. exposed ports for container config. +func (s *TrialSpec) MakeEnvPorts() expconf.EnvironmentConfigV0 { + ppc := s.ProxyPorts() + ports := s.ExperimentConfig.Environment().Ports() + if ports == nil { + ports = map[string]int{} + } + + for _, pp := range ppc { + port := pp.ProxyPort() + ports[strconv.Itoa(port)] = port + } + + // TODO: remove this, but without breaking rendezvous api. + ports["trial"] = 1734 + + env := s.ExperimentConfig.Environment() + env.SetPorts(ports) + + return env +} + +// TrialSpecProxyPorts combines user-defined and system proxy configs. +// This static function is public because trial actor builds `TrialSpec` instances late. +func TrialSpecProxyPorts( + taskSpec *TaskSpec, + expConfig expconf.ExperimentConfigV0, +) expconf.ProxyPortsConfig { + env := expConfig.Environment() + epp := schemas.WithDefaults(taskSpec.ExtraProxyPorts) + out := make(expconf.ProxyPortsConfig, 0, len(epp)+len(env.ProxyPorts())) + + for _, pp := range epp { + out = append(out, pp) + } + + for _, pp := range env.ProxyPorts() { + out = append(out, pp) + } + + return out +} + +// ProxyPorts combines user-defined and system proxy configs. +func (s *TrialSpec) ProxyPorts() expconf.ProxyPortsConfig { + return TrialSpecProxyPorts(&s.Base, s.ExperimentConfig) +} diff --git a/master/static/migrations/20230228153343_metrics-column-cleanup.tx.down.sql b/master/static/migrations/20230228153343_metrics-column-cleanup.tx.down.sql new file mode 100644 index 00000000000..4ceac4ebbee --- /dev/null +++ b/master/static/migrations/20230228153343_metrics-column-cleanup.tx.down.sql @@ -0,0 +1,80 @@ +DROP VIEW trials_augmented_view; + +DROP VIEW steps; +DROP VIEW validations; +DROP VIEW checkpoints; + +CREATE TYPE public.step_state AS ENUM ( + 'ACTIVE', + 'COMPLETED', + 'ERROR' +); + +CREATE TYPE public.validation_state AS ENUM ( + 'ACTIVE', + 'COMPLETED', + 'ERROR' +); + +ALTER TABLE raw_steps + ADD COLUMN total_records integer NOT NULL DEFAULT 0, + ADD COLUMN total_epochs real NOT NULL DEFAULT 0, + ADD COLUMN computed_records integer NULL, + ADD COLUMN state public.step_state NOT NULL DEFAULT 'COMPLETED'; + +ALTER TABLE raw_validations + ADD COLUMN total_records integer NOT NULL DEFAULT 0, + ADD COLUMN total_epochs real NOT NULL DEFAULT 0, + ADD COLUMN computed_records integer NULL, + ADD COLUMN state public.validation_state NOT NULL DEFAULT 'COMPLETED'; + +ALTER TABLE raw_checkpoints + ADD COLUMN total_records integer NOT NULL DEFAULT 0, + ADD COLUMN total_epochs real NOT NULL DEFAULT 0; + +CREATE VIEW steps AS + SELECT * FROM raw_steps WHERE NOT archived; +CREATE VIEW validations AS + SELECT * FROM raw_validations WHERE NOT archived; +CREATE VIEW checkpoints AS + SELECT * FROM raw_checkpoints WHERE NOT archived; + +-- Copy from static/migrations/20220922114430_trials-collection.tx.up.sql +CREATE VIEW public.trials_augmented_view AS + WITH b AS ( + select trial_id, max(total_batches) total_batches from steps group by trial_id + ) + SELECT + t.id AS trial_id, + t.state AS state, + t.hparams AS hparams, + jsonb_collect(s.metrics->'avg_metrics') AS training_metrics, + jsonb_collect(v.metrics->'validation_metrics') AS validation_metrics, + t.tags AS tags, + t.start_time AS start_time, + t.end_time AS end_time, + max(e.config->'searcher'->>'name') as searcher_type, + max(e.id) AS experiment_id, + max(e.config->>'name') AS experiment_name, + max(e.config->>'description') AS experiment_description, + -- there's only one + jsonb_agg(e.config ->> 'labels'::text) AS experiment_labels, + max(e.owner_id) AS user_id, + max(e.project_id) AS project_id, + max(p.workspace_id) AS workspace_id, + -- temporary + max(b.total_batches) as total_batches, + max(e.config->'searcher'->>'metric') AS searcher_metric, + max(v.metrics->'validation_metrics'->>(e.config->'searcher'->>'metric'))::float8 AS searcher_metric_value, + max(CASE + WHEN coalesce((config->'searcher'->>'smaller_is_better')::boolean, true) + THEN (v.metrics->'validation_metrics'->>(e.config->'searcher'->>'metric'))::float8 + ELSE -1.0 * (v.metrics->'validation_metrics'->>(e.config->'searcher'->>'metric'))::float8 + END) AS searcher_metric_loss + FROM trials t + LEFT JOIN experiments e ON t.experiment_id = e.id + LEFT JOIN projects p ON e.project_id = p.id + LEFT JOIN validations v ON t.id = v.trial_id AND v.id = t.best_validation_id + LEFT JOIN steps s on t.id = s.trial_id AND v.total_batches = s.total_batches + LEFT JOIN b on t.id = b.trial_id + GROUP BY t.id; diff --git a/master/static/migrations/20230228153343_metrics-column-cleanup.tx.up.sql b/master/static/migrations/20230228153343_metrics-column-cleanup.tx.up.sql new file mode 100644 index 00000000000..2249e8a7feb --- /dev/null +++ b/master/static/migrations/20230228153343_metrics-column-cleanup.tx.up.sql @@ -0,0 +1,74 @@ +DROP VIEW trials_augmented_view; + +DROP VIEW steps; +DROP VIEW validations; +DROP VIEW checkpoints; + +DELETE FROM raw_steps WHERE state != 'COMPLETED'; +DELETE FROM raw_validations WHERE state != 'COMPLETED'; + +ALTER TABLE raw_steps + DROP COLUMN total_records, + DROP COLUMN total_epochs, + DROP COLUMN state, + DROP COLUMN computed_records; + +ALTER TABLE raw_validations + DROP COLUMN total_records, + DROP COLUMN total_epochs, + DROP COLUMN state, + DROP COLUMN computed_records; + +ALTER TABLE raw_checkpoints + DROP COLUMN total_epochs, + DROP COLUMN total_records; + +DROP TYPE step_state; +DROP TYPE validation_state; + +CREATE VIEW steps AS + SELECT * FROM raw_steps WHERE NOT archived; +CREATE VIEW validations AS + SELECT * FROM raw_validations WHERE NOT archived; +CREATE VIEW checkpoints AS + SELECT * FROM raw_checkpoints WHERE NOT archived; + +-- Copy from static/migrations/20220922114430_trials-collection.tx.up.sql +CREATE VIEW public.trials_augmented_view AS + WITH b AS ( + select trial_id, max(total_batches) total_batches from steps group by trial_id + ) + SELECT + t.id AS trial_id, + t.state AS state, + t.hparams AS hparams, + jsonb_collect(s.metrics->'avg_metrics') AS training_metrics, + jsonb_collect(v.metrics->'validation_metrics') AS validation_metrics, + t.tags AS tags, + t.start_time AS start_time, + t.end_time AS end_time, + max(e.config->'searcher'->>'name') as searcher_type, + max(e.id) AS experiment_id, + max(e.config->>'name') AS experiment_name, + max(e.config->>'description') AS experiment_description, + -- there's only one + jsonb_agg(e.config ->> 'labels'::text) AS experiment_labels, + max(e.owner_id) AS user_id, + max(e.project_id) AS project_id, + max(p.workspace_id) AS workspace_id, + -- temporary + max(b.total_batches) as total_batches, + max(e.config->'searcher'->>'metric') AS searcher_metric, + max(v.metrics->'validation_metrics'->>(e.config->'searcher'->>'metric'))::float8 AS searcher_metric_value, + max(CASE + WHEN coalesce((config->'searcher'->>'smaller_is_better')::boolean, true) + THEN (v.metrics->'validation_metrics'->>(e.config->'searcher'->>'metric'))::float8 + ELSE -1.0 * (v.metrics->'validation_metrics'->>(e.config->'searcher'->>'metric'))::float8 + END) AS searcher_metric_loss + FROM trials t + LEFT JOIN experiments e ON t.experiment_id = e.id + LEFT JOIN projects p ON e.project_id = p.id + LEFT JOIN validations v ON t.id = v.trial_id AND v.id = t.best_validation_id + LEFT JOIN steps s on t.id = s.trial_id AND v.total_batches = s.total_batches + LEFT JOIN b on t.id = b.trial_id + GROUP BY t.id; diff --git a/master/static/srv/enrich_task_logs.py b/master/static/srv/enrich_task_logs.py index 549a3942b5f..a7284f774f3 100644 --- a/master/static/srv/enrich_task_logs.py +++ b/master/static/srv/enrich_task_logs.py @@ -63,8 +63,11 @@ def run(self) -> None: m = rank.match(line) if m: - parsed_metadata["rank"] = m.group("rank_id") - line = m.group("log") + try: + parsed_metadata["rank_id"] = int(m.group("rank_id")) + line = m.group("log") + except ValueError: + pass m = level.match(line) if m: @@ -74,7 +77,7 @@ def run(self) -> None: self.ship_queue.put( { "timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(), - "log": line + "\n", + "log": line if line.endswith("\n") else line + "\n", **self.task_logging_metadata, **parsed_metadata, } @@ -173,6 +176,10 @@ def main( task_logging_metadata = json.loads(task_logging_metadata_json) task_logging_metadata["stdtype"] = args.stdtype task_logging_metadata["agent_id"] = socket.gethostname() + task_logging_metadata["source"] = "task" + container_id = os.environ.get("DET_CONTAINER_ID") + if container_id is not None: + task_logging_metadata["container_id"] = container_id # If trial exists, just drop it since it could mess with de-ser on the API end. task_logging_metadata.pop("trial_id", None) diff --git a/master/static/srv/get_trial.sql b/master/static/srv/get_trial.sql index 81d04569f28..a5e652caf70 100644 --- a/master/static/srv/get_trial.sql +++ b/master/static/srv/get_trial.sql @@ -15,7 +15,6 @@ FROM FROM (SELECT s.id, s.trial_id, - s.state, s.end_time, s.total_batches, @@ -38,7 +37,6 @@ FROM (SELECT v.id, v.trial_id, v.total_batches, - v.state, v.end_time, v.metrics FROM validations v diff --git a/master/static/srv/get_trial_metrics.sql b/master/static/srv/get_trial_metrics.sql index 96ac5cee38b..8b546370d23 100644 --- a/master/static/srv/get_trial_metrics.sql +++ b/master/static/srv/get_trial_metrics.sql @@ -15,7 +15,6 @@ FROM (SELECT s.id, s.total_batches, s.trial_id, - s.state, s.end_time, s.total_batches, s.metrics, @@ -39,7 +38,6 @@ FROM (SELECT v.id, v.trial_id, v.total_batches, - v.state, v.end_time, v.metrics FROM validations v diff --git a/master/static/srv/proto_experiment_validation_history.sql b/master/static/srv/proto_experiment_validation_history.sql index 1b949e79e74..495c4734bb6 100644 --- a/master/static/srv/proto_experiment_validation_history.sql +++ b/master/static/srv/proto_experiment_validation_history.sql @@ -10,11 +10,11 @@ SELECT (WITH const AS ( END) AS sign FROM experiments WHERE id = e.id ), vals AS ( - SELECT v.trial_id, v.end_time, v.state, + SELECT v.trial_id, v.end_time, (v.metrics->'validation_metrics'->>(const.metric_name))::float8 AS searcher_metric FROM validations v, trials t, const - WHERE v.trial_id = t.id and t.experiment_id = e.id and v.state = 'COMPLETED' + WHERE v.trial_id = t.id and t.experiment_id = e.id ) SELECT coalesce(jsonb_agg(v), '[]'::jsonb) FROM ( @@ -29,7 +29,6 @@ SELECT (WITH const AS ( trials t, const WHERE v.trial_id = t.id - AND v.state = 'COMPLETED' AND t.experiment_id = e.id ) n, const WHERE const.sign * n.searcher_metric < n.prev_min_error diff --git a/master/static/srv/proto_get_trial_ids_for_experiment.sql b/master/static/srv/proto_get_trial_ids_for_experiment.sql index 3fc3121f566..c9d48bb377a 100644 --- a/master/static/srv/proto_get_trial_ids_for_experiment.sql +++ b/master/static/srv/proto_get_trial_ids_for_experiment.sql @@ -25,7 +25,7 @@ WITH searcher_info AS ( ( SELECT coalesce(max(s.total_batches), 0) FROM steps s - WHERE s.trial_id = t.id AND s.state = 'COMPLETED' + WHERE s.trial_id = t.id ) AS total_batches_processed, ( CASE WHEN t.best_validation_id IS NOT NULL THEN @@ -46,7 +46,6 @@ WITH searcher_info AS ( SELECT searcher_info.sign * (v.metrics->'validation_metrics'->>searcher_info.metric_name)::float8 FROM validations v WHERE v.trial_id = t.id - AND v.state = 'COMPLETED' ORDER BY v.id DESC LIMIT 1 ) as latest_signed_search_metric diff --git a/master/static/srv/proto_get_trial_workloads.sql b/master/static/srv/proto_get_trial_workloads.sql index 8bd4617c7cb..ba7750002c1 100644 --- a/master/static/srv/proto_get_trial_workloads.sql +++ b/master/static/srv/proto_get_trial_workloads.sql @@ -1,7 +1,7 @@ WITH validations_vt AS ( SELECT row_to_json(r1) AS validation, total_batches, end_time, metrics FROM ( - SELECT 'STATE_' || v.state as state, + SELECT v.end_time, v.total_batches, v.metrics->'num_inputs' as num_inputs, @@ -14,7 +14,6 @@ trainings_vt AS ( SELECT row_to_json(r1) AS training, total_batches, end_time, metrics FROM ( SELECT s.end_time, - 'STATE_' || s.state as state, CASE WHEN $5 = true THEN s.metrics diff --git a/master/static/srv/proto_get_trials_plus.sql b/master/static/srv/proto_get_trials_plus.sql index 023e2acc2fe..864b97bce4d 100644 --- a/master/static/srv/proto_get_trials_plus.sql +++ b/master/static/srv/proto_get_trials_plus.sql @@ -27,7 +27,6 @@ trial_validations AS ( SELECT v.trial_id, v.total_batches, v.end_time, - v.state, v.metrics, ( ( @@ -36,16 +35,14 @@ trial_validations AS ( ) AS signed_searcher_metric FROM validations v INNER JOIN searcher_info ON v.trial_id = searcher_info.trial_id - WHERE v.state = 'COMPLETED' - AND ( - v.metrics->'validation_metrics'->>(searcher_info.metric_name) - ) IS NOT NULL + WHERE ( + v.metrics->'validation_metrics'->>(searcher_info.metric_name) + ) IS NOT NULL ), best_validation AS ( SELECT v.trial_id, v.total_batches, v.end_time, - 'STATE_' || v.state AS state, json_build_object('avg_metrics', v.metrics->'validation_metrics') as metrics, v.metrics->'num_inputs' as num_inputs FROM ( @@ -63,7 +60,6 @@ latest_validation AS ( SELECT v.trial_id, v.total_batches, v.end_time, - 'STATE_' || v.state AS state, json_build_object('avg_metrics', v.metrics->'validation_metrics') as metrics, v.metrics->'num_inputs' as num_inputs FROM ( @@ -133,7 +129,6 @@ latest_training AS ( SELECT s.trial_id, s.total_batches, s.end_time, - 'STATE_' || s.state AS state, json_build_object('avg_metrics', s.metrics->'avg_metrics') as metrics FROM ( SELECT s.*, @@ -143,7 +138,6 @@ latest_training AS ( ) AS rank FROM steps s INNER JOIN searcher_info ON s.trial_id = searcher_info.trial_id - WHERE s.state = 'COMPLETED' ) s JOIN searcher_info ON searcher_info.trial_id = s.trial_id WHERE s.rank = 1 @@ -167,7 +161,6 @@ SELECT SELECT s.total_batches FROM steps s WHERE s.trial_id = t.id - AND s.state = 'COMPLETED' ORDER BY s.total_batches DESC LIMIT 1 ) AS total_batches_processed, diff --git a/master/static/srv/test_insert_huge_metrics.sql b/master/static/srv/test_insert_huge_metrics.sql index e381b618a8c..7d7d0d73b16 100644 --- a/master/static/srv/test_insert_huge_metrics.sql +++ b/master/static/srv/test_insert_huge_metrics.sql @@ -1,11 +1,11 @@ WITH ss AS ( - SELECT trial_id, state, end_time, metrics, total_batches, total_records, total_epochs, trial_run_id, archived, computed_records + SELECT trial_id, end_time, metrics, total_batches, trial_run_id, archived FROM steps WHERE trial_id=$1 ORDER BY total_batches DESC LIMIT 1) INSERT INTO steps - (trial_id, state, end_time, metrics, total_batches, total_records, total_epochs, trial_run_id, archived, computed_records) - SELECT trial_id, state, end_time, metrics, total_batches+g, total_records, total_epochs, trial_run_id, archived, computed_records + (trial_id, end_time, metrics, total_batches, trial_run_id, archived) + SELECT trial_id, end_time, metrics, total_batches+g, trial_run_id, archived FROM ss, generate_series(1, $2) g RETURNING false; diff --git a/model_hub/Makefile b/model_hub/Makefile index 28ddbfd1677..286b9563a46 100644 --- a/model_hub/Makefile +++ b/model_hub/Makefile @@ -5,7 +5,7 @@ SHORT_GIT_HASH := $(shell git rev-parse --short HEAD) ARTIFACTS_DIR := /tmp/artifacts # Model-hub library environments will be built on top of the default GPU and CPU images in master/pkg/model/defaults.go -DEFAULT_GPU_IMAGE := determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c +DEFAULT_GPU_IMAGE := determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 ############REMINDER############ # When bumping third-party library versions, remember to bump versions in diff --git a/model_hub/examples/huggingface/language-modeling/clm_config.yaml b/model_hub/examples/huggingface/language-modeling/clm_config.yaml index aa8bfbba93e..db5d7d6b818 100644 --- a/model_hub/examples/huggingface/language-modeling/clm_config.yaml +++ b/model_hub/examples/huggingface/language-modeling/clm_config.yaml @@ -33,7 +33,7 @@ searcher: smaller_is_better: true environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 4 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/language-modeling/mlm_config.yaml b/model_hub/examples/huggingface/language-modeling/mlm_config.yaml index 682835794a3..0d03f6b8fad 100644 --- a/model_hub/examples/huggingface/language-modeling/mlm_config.yaml +++ b/model_hub/examples/huggingface/language-modeling/mlm_config.yaml @@ -36,7 +36,7 @@ searcher: smaller_is_better: true environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/language-modeling/plm_config.yaml b/model_hub/examples/huggingface/language-modeling/plm_config.yaml index f6f29053744..8ebd1a45808 100644 --- a/model_hub/examples/huggingface/language-modeling/plm_config.yaml +++ b/model_hub/examples/huggingface/language-modeling/plm_config.yaml @@ -37,7 +37,7 @@ searcher: smaller_is_better: true environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 2 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/multiple-choice/swag_config.yaml b/model_hub/examples/huggingface/multiple-choice/swag_config.yaml index 85a5b13e4be..efac7d945bd 100644 --- a/model_hub/examples/huggingface/multiple-choice/swag_config.yaml +++ b/model_hub/examples/huggingface/multiple-choice/swag_config.yaml @@ -33,7 +33,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 2 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/question-answering/squad.yaml b/model_hub/examples/huggingface/question-answering/squad.yaml index 1f456f03fa4..30de5ef2928 100644 --- a/model_hub/examples/huggingface/question-answering/squad.yaml +++ b/model_hub/examples/huggingface/question-answering/squad.yaml @@ -38,7 +38,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/question-answering/squad_beam_search.yaml b/model_hub/examples/huggingface/question-answering/squad_beam_search.yaml index 992b9c539ea..14e408ddcfd 100644 --- a/model_hub/examples/huggingface/question-answering/squad_beam_search.yaml +++ b/model_hub/examples/huggingface/question-answering/squad_beam_search.yaml @@ -37,7 +37,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/question-answering/squad_distributed.yaml b/model_hub/examples/huggingface/question-answering/squad_distributed.yaml index eae8489d4ae..eadfe42df9d 100644 --- a/model_hub/examples/huggingface/question-answering/squad_distributed.yaml +++ b/model_hub/examples/huggingface/question-answering/squad_distributed.yaml @@ -38,7 +38,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 8 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/question-answering/squad_v2.yaml b/model_hub/examples/huggingface/question-answering/squad_v2.yaml index 6b7a1170a61..29ac1e3c577 100644 --- a/model_hub/examples/huggingface/question-answering/squad_v2.yaml +++ b/model_hub/examples/huggingface/question-answering/squad_v2.yaml @@ -38,7 +38,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/question-answering/squad_v2_albert.yaml b/model_hub/examples/huggingface/question-answering/squad_v2_albert.yaml index 0a9ce5794c4..fb91ac6acfc 100644 --- a/model_hub/examples/huggingface/question-answering/squad_v2_albert.yaml +++ b/model_hub/examples/huggingface/question-answering/squad_v2_albert.yaml @@ -40,7 +40,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 8 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/question-answering/squad_v2_beam_search.yaml b/model_hub/examples/huggingface/question-answering/squad_v2_beam_search.yaml index 1589fa48c42..8c6fb27cec1 100644 --- a/model_hub/examples/huggingface/question-answering/squad_v2_beam_search.yaml +++ b/model_hub/examples/huggingface/question-answering/squad_v2_beam_search.yaml @@ -38,7 +38,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/text-classification/glue_config.yaml b/model_hub/examples/huggingface/text-classification/glue_config.yaml index 2d4ce4c4d5f..3c1c4610206 100644 --- a/model_hub/examples/huggingface/text-classification/glue_config.yaml +++ b/model_hub/examples/huggingface/text-classification/glue_config.yaml @@ -45,7 +45,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/text-classification/xnli_config.yaml b/model_hub/examples/huggingface/text-classification/xnli_config.yaml index 4f2016e5f2f..bea6c0748e2 100644 --- a/model_hub/examples/huggingface/text-classification/xnli_config.yaml +++ b/model_hub/examples/huggingface/text-classification/xnli_config.yaml @@ -36,7 +36,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 2 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/huggingface/token-classification/ner_config.yaml b/model_hub/examples/huggingface/token-classification/ner_config.yaml index 111a8491626..9c309c344ad 100644 --- a/model_hub/examples/huggingface/token-classification/ner_config.yaml +++ b/model_hub/examples/huggingface/token-classification/ner_config.yaml @@ -34,7 +34,7 @@ searcher: smaller_is_better: false environment: image: - gpu: determinedai/model-hub-transformers:0.19.12-dev0 + gpu: determinedai/model-hub-transformers:0.20.1-dev0 resources: slots_per_trial: 1 # We add a bind_mount here so that cached data, tokenized data, and models will be saved to the diff --git a/model_hub/examples/mmdetection/fasterrcnn.yaml b/model_hub/examples/mmdetection/fasterrcnn.yaml index 76a7fb0fc22..72d97d34063 100644 --- a/model_hub/examples/mmdetection/fasterrcnn.yaml +++ b/model_hub/examples/mmdetection/fasterrcnn.yaml @@ -40,7 +40,7 @@ searcher: max_restarts: 5 environment: image: - gpu: determinedai/model-hub-mmdetection:0.19.12-dev0 + gpu: determinedai/model-hub-mmdetection:0.20.1-dev0 environment_variables: - OMP_NUM_THREADS=1 # Following pytorch dtrain, this environment variable is set to 1 to avoid overloading the system. diff --git a/model_hub/examples/mmdetection/hydra/configs/config.yaml b/model_hub/examples/mmdetection/hydra/configs/config.yaml index e0cff4725de..fbed718b36a 100644 --- a/model_hub/examples/mmdetection/hydra/configs/config.yaml +++ b/model_hub/examples/mmdetection/hydra/configs/config.yaml @@ -14,7 +14,7 @@ min_validation_period: environment: image: - gpu: determinedai/model-hub-mmdetection:0.19.12-dev0 + gpu: determinedai/model-hub-mmdetection:0.20.1-dev0 environment_variables: - OMP_NUM_THREADS=1 # Following pytorch dtrain, this environment variable is set to 1 to avoid overloading the system. diff --git a/model_hub/examples/mmdetection/maskrcnn.yaml b/model_hub/examples/mmdetection/maskrcnn.yaml index 3787da66f4e..3f9cada1093 100644 --- a/model_hub/examples/mmdetection/maskrcnn.yaml +++ b/model_hub/examples/mmdetection/maskrcnn.yaml @@ -40,7 +40,7 @@ searcher: max_restarts: 5 environment: image: - gpu: determinedai/model-hub-mmdetection:0.19.12-dev0 + gpu: determinedai/model-hub-mmdetection:0.20.1-dev0 environment_variables: - OMP_NUM_THREADS=1 # Following pytorch dtrain, this environment variable is set to 1 to avoid overloading the system. diff --git a/model_hub/examples/mmdetection/panoptic_fpn.yaml b/model_hub/examples/mmdetection/panoptic_fpn.yaml index a82e5556b2b..8b65e6e5b2c 100644 --- a/model_hub/examples/mmdetection/panoptic_fpn.yaml +++ b/model_hub/examples/mmdetection/panoptic_fpn.yaml @@ -40,7 +40,7 @@ searcher: max_restarts: 5 environment: image: - gpu: determinedai/model-hub-mmdetection:0.19.12-dev0 + gpu: determinedai/model-hub-mmdetection:0.20.1-dev0 environment_variables: - OMP_NUM_THREADS=1 # Following pytorch dtrain, this environment variable is set to 1 to avoid overloading the system. diff --git a/model_hub/examples/mmdetection/yolov3.yaml b/model_hub/examples/mmdetection/yolov3.yaml index eabc247995e..853595cf0dd 100644 --- a/model_hub/examples/mmdetection/yolov3.yaml +++ b/model_hub/examples/mmdetection/yolov3.yaml @@ -40,7 +40,7 @@ searcher: max_restarts: 5 environment: image: - gpu: determinedai/model-hub-mmdetection:0.19.12-dev0 + gpu: determinedai/model-hub-mmdetection:0.20.1-dev0 environment_variables: - OMP_NUM_THREADS=1 # Following pytorch dtrain, this environment variable is set to 1 to avoid overloading the system. diff --git a/model_hub/model_hub/__version__.py b/model_hub/model_hub/__version__.py index 35775c59a65..d4f7707f462 100644 --- a/model_hub/model_hub/__version__.py +++ b/model_hub/model_hub/__version__.py @@ -1 +1 @@ -__version__ = "0.19.12-dev0" +__version__ = "0.20.1-dev0" diff --git a/model_hub/setup.py b/model_hub/setup.py index 2a2bc11c4e6..fcd50e73fa4 100644 --- a/model_hub/setup.py +++ b/model_hub/setup.py @@ -2,7 +2,7 @@ setup( name="model-hub", - version="0.19.12-dev0", + version="0.20.1-dev0", author="Determined AI", author_email="hello@determined.ai", url="https://determined.ai/", diff --git a/pre-commit/web_lint_check.py b/pre-commit/web_lint_check.py index 4869161d7bc..9185e6e7684 100755 --- a/pre-commit/web_lint_check.py +++ b/pre-commit/web_lint_check.py @@ -4,36 +4,42 @@ import multiprocessing import os import subprocess +from typing import List def web_lint_check(): parser = argparse.ArgumentParser(description="Lint Check for Web") - parser.add_argument("target", help="Either js, css, or misc", choices=["js", "css", "misc"]) - parser.add_argument("file_paths", help="1 or more file paths", nargs="+") + parser.add_argument( + "target", help="Either js, css, or misc", type=str, choices=["js", "css", "misc"] + ) + parser.add_argument("file_paths", help="1 or more file paths", nargs="+", default=[]) args = parser.parse_args() DIR = "webui/react/" target: str = args.target - file_paths: str = " ".join([os.path.relpath(file_path, DIR) for file_path in args.file_paths]) + file_paths: List[str] = args.file_paths + rel_file_paths: str = " ".join([os.path.relpath(file_path, DIR) for file_path in file_paths]) nproc: int = multiprocessing.cpu_count() - run_command: list[str] = [ + run_command: List[str] = [ "make", f"-j{nproc}", "-C", DIR, "prettier", - f"PRE_ARGS=-- -c {file_paths}", + f"PRE_ARGS=-- --write -c {rel_file_paths}", ] # TODO: replace it with `match` if we support python v3.10 if target == "js": - run_command += ["eslint", f"ES_ARGS={file_paths}"] + run_command += ["eslint", f"ES_ARGS=-- --fix {rel_file_paths}"] elif target == "css": - run_command += ["stylelint", f"ST_ARGS={file_paths}"] + run_command += ["stylelint", f"ST_ARGS=-- --fix {rel_file_paths}"] elif target == "misc": run_command += ["check-package-lock"] returncode: int = subprocess.call(run_command) + if returncode == 0: + returncode = subprocess.call(["git", "add"] + file_paths) exit(returncode) diff --git a/proto/buf.image.bin b/proto/buf.image.bin index 4db3e7e35f4..bdf1510c04f 100644 Binary files a/proto/buf.image.bin and b/proto/buf.image.bin differ diff --git a/proto/go.mod b/proto/go.mod index b1f02baccf8..9e196ccb27c 100644 --- a/proto/go.mod +++ b/proto/go.mod @@ -11,7 +11,7 @@ require ( ) require ( - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect - golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect - golang.org/x/text v0.3.5 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect ) diff --git a/proto/go.sum b/proto/go.sum index 80942cf078c..639e6e82e89 100644 --- a/proto/go.sum +++ b/proto/go.sum @@ -63,8 +63,9 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -77,13 +78,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/proto/pkg/apiv1/api.pb.go b/proto/pkg/apiv1/api.pb.go index 300c80db5f9..0c3ab525de7 100644 --- a/proto/pkg/apiv1/api.pb.go +++ b/proto/pkg/apiv1/api.pb.go @@ -76,7 +76,7 @@ var file_determined_api_v1_api_proto_rawDesc = []byte{ 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x24, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x70, - 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x9d, 0xee, 0x01, 0x0a, 0x0a, 0x44, + 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xa9, 0xeb, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x7e, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, @@ -1469,18 +1469,6 @@ var file_determined_api_v1_api_proto_rawDesc = []byte{ 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x30, 0x01, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, - 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x30, 0x01, 0x12, 0xb5, 0x01, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, @@ -1516,535 +1504,524 @@ var file_determined_api_v1_api_proto_rawDesc = []byte{ 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x2d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x92, 0x41, 0x0a, 0x0a, - 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x30, 0x01, 0x12, 0xb1, 0x01, 0x0a, 0x16, - 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, - 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, - 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x70, - 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, - 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x30, 0x01, 0x12, - 0x98, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, - 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x30, 0x01, 0x12, 0x98, 0x01, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x73, + 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x92, 0x41, 0x0a, - 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0xc8, 0x01, 0x0a, 0x13, 0x43, - 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, - 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, 0x49, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3f, 0x22, 0x3d, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x76, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, - 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x68, - 0x79, 0x70, 0x65, 0x72, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0xbe, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3f, 0x12, 0x3d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, - 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x68, 0x79, - 0x70, 0x65, 0x72, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x30, 0x01, 0x12, 0xb3, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x77, - 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x37, 0x88, 0x02, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x61, 0x77, - 0x92, 0x41, 0x09, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0xcc, 0x01, 0x0a, - 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x12, 0x36, 0x2e, + 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6f, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x18, 0x12, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2d, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0xc8, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2d, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x3f, 0x22, 0x3d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x68, 0x79, 0x70, 0x65, 0x72, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x12, 0xbe, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x3f, 0x12, 0x3d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, + 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x68, 0x79, 0x70, 0x65, 0x72, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x30, 0x01, 0x12, 0xb3, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x77, 0x12, 0x2f, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x12, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x92, - 0x41, 0x09, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x8f, 0x01, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x2e, 0x64, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x37, 0x88, 0x02, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x6c, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x61, 0x77, 0x92, 0x41, 0x09, 0x0a, + 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0xcc, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x12, 0x36, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x37, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6c, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x29, 0x12, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x92, 0x41, 0x09, 0x0a, 0x07, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x8f, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x12, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xb0, 0x01, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x92, 0x41, 0x0c, + 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x92, 0x41, 0x0c, + 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x90, 0x01, 0x0a, + 0x0d, 0x50, 0x6f, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, + 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, + 0xa0, 0x01, 0x0a, 0x0e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x57, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x32, + 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x12, 0x98, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, - 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xb0, 0x01, - 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, - 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x12, 0x8d, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x12, 0x90, 0x01, 0x0a, 0x0d, 0x50, 0x6f, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x6f, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x12, 0xa0, 0x01, 0x0a, 0x0e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x57, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, - 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x24, 0x32, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x09, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x98, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, - 0x64, 0x7d, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x12, 0xa3, 0x01, 0x0a, 0x10, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, - 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xa3, 0x01, + 0x0a, 0x10, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x22, 0x1f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, - 0x2f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xab, 0x01, 0x0a, 0x12, 0x55, 0x6e, 0x61, 0x72, - 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2c, - 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x23, 0x22, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x57, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, - 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, - 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x69, 0x6e, 0x92, 0x41, 0x0c, 0x0a, - 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x9b, 0x01, 0x0a, 0x0e, - 0x55, 0x6e, 0x70, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x28, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x70, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x70, + 0x76, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x21, 0x22, 0x1f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x72, 0x63, + 0x68, 0x69, 0x76, 0x65, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x12, 0xab, 0x01, 0x0a, 0x12, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x61, + 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, + 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, + 0x76, 0x65, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x12, 0x93, 0x01, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1d, 0x2f, 0x61, 0x70, + 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x70, 0x69, 0x6e, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, - 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x12, 0xa0, 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x2a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x12, 0xa5, 0x01, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x3a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x92, - 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0xa5, 0x01, 0x0a, - 0x0f, 0x50, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x73, - 0x12, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, - 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x1a, - 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6e, - 0x6f, 0x74, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x12, 0x94, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x69, 0x6e, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x9b, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x70, 0x69, + 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x6e, 0x70, 0x69, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x70, 0x69, 0x6e, 0x57, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x2f, 0x75, 0x6e, 0x70, 0x69, 0x6e, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0xa0, 0x01, + 0x0a, 0x0b, 0x50, 0x6f, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x32, 0x15, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x92, 0x41, - 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x0d, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x27, 0x2e, + 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x2a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x12, 0xa5, 0x01, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, + 0x6f, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x31, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, + 0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x3a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0xa5, 0x01, 0x0a, 0x0f, 0x50, 0x75, 0x74, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x1a, 0x23, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6e, 0x6f, 0x74, 0x65, 0x73, + 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x12, 0x94, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, + 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x32, 0x15, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x3a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x17, 0x2a, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0a, 0x0a, 0x08, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x99, 0x01, 0x0a, 0x0e, 0x41, 0x72, 0x63, + 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x2a, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, - 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x99, 0x01, 0x0a, - 0x0e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, - 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, - 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1d, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0xa1, 0x01, 0x0a, 0x10, 0x55, 0x6e, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2a, 0x2e, - 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, + 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x12, 0xa1, 0x01, 0x0a, 0x10, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, + 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x22, 0x1f, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x92, - 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x98, 0x01, 0x0a, - 0x0b, 0x4d, 0x6f, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x2e, 0x64, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x27, 0x22, 0x22, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x6d, 0x6f, 0x76, 0x65, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0xaa, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x76, 0x65, - 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x6f, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x45, 0x78, 0x70, - 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, - 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x6f, 0x76, - 0x65, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0d, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x83, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x57, 0x65, 0x62, 0x68, - 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x65, 0x62, 0x68, - 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x92, 0x41, 0x0a, - 0x0a, 0x08, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x0b, 0x50, - 0x6f, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x6f, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1b, 0x22, 0x10, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, - 0x6f, 0x6b, 0x73, 0x3a, 0x07, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x92, 0x41, 0x0a, 0x0a, - 0x08, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x0d, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x27, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, - 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x2a, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0a, - 0x0a, 0x08, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x0b, 0x54, - 0x65, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x65, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1c, 0x22, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, - 0x6f, 0x6b, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x92, 0x41, 0x0a, - 0x0a, 0x08, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x83, 0x01, 0x0a, 0x08, 0x47, - 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x22, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x12, 0x85, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x23, - 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x84, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, + 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x22, 0x1f, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x2f, 0x75, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x92, 0x41, 0x0a, 0x0a, 0x08, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x98, 0x01, 0x0a, 0x0b, 0x4d, 0x6f, 0x76, + 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, + 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, - 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x3a, - 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, - 0x8f, 0x01, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, - 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x1a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, - 0x7d, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x12, 0x8c, 0x01, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x12, 0xa8, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x22, + 0x22, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, + 0x6f, 0x76, 0x65, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x12, 0xaa, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x45, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x6f, 0x76, 0x65, 0x3a, 0x01, 0x2a, + 0x92, 0x41, 0x0d, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x83, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, + 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, + 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x57, 0x65, + 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x73, 0x74, 0x57, + 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x57, + 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x10, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x3a, + 0x07, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x57, 0x65, 0x62, + 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x17, 0x2a, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x68, + 0x6f, 0x6f, 0x6b, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x57, 0x65, + 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x57, + 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, + 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x1a, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x57, 0x65, + 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x83, 0x01, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x12, 0x22, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x92, + 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x85, 0x01, 0x0a, + 0x09, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x23, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x84, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xe0, 0x01, 0x0a, 0x24, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, - 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x3e, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0x90, - 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x42, 0x79, 0x49, 0x44, 0x12, - 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x42, 0x79, 0x49, 0x44, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, - 0x6f, 0x6c, 0x65, 0x73, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, - 0x79, 0x2d, 0x69, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, - 0x43, 0x12, 0xb6, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x64, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, - 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, + 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x8f, 0x01, 0x0a, 0x0b, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x25, 0x2e, 0x64, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1e, 0x1a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x01, 0x2a, + 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x8c, 0x01, + 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x25, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x92, + 0x41, 0x0a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0xa8, 0x01, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x92, 0x41, + 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xe0, 0x01, 0x0a, 0x24, 0x47, 0x65, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x41, 0x73, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x3e, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x41, 0x6e, + 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x41, 0x6e, + 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, - 0x62, 0x79, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x7d, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xbb, 0x01, 0x0a, 0x17, 0x47, + 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0x90, 0x01, 0x0a, 0x0c, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x42, 0x79, 0x49, 0x44, 0x12, 0x26, 0x2e, 0x64, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, + 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, + 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x79, 0x2d, 0x69, 0x64, + 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xb6, 0x01, + 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, - 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x31, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, + 0x6f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x79, 0x2d, 0x75, + 0x73, 0x65, 0x72, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x06, + 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xbb, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x12, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x79, 0x2d, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x92, - 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xca, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x54, 0x6f, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x36, 0x2e, 0x64, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x54, 0x6f, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x37, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x6f, 0x6c, 0x65, - 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x6f, 0x53, 0x63, 0x6f, - 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x2a, 0x22, 0x25, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, - 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x79, 0x2d, 0x61, 0x73, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, - 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0x80, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, - 0x6c, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01, 0x2a, 0x92, - 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0x8f, 0x01, 0x0a, 0x0b, 0x41, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, - 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x61, - 0x64, 0x64, 0x2d, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x01, - 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xa4, 0x01, 0x0a, 0x11, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x75, 0x70, 0x12, 0x31, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, + 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, + 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x2a, 0x12, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x79, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x06, 0x0a, 0x04, + 0x52, 0x42, 0x41, 0x43, 0x12, 0xca, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, + 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x6f, + 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x36, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, + 0x6f, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x25, 0x22, 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, - 0x43, 0x12, 0x98, 0x01, 0x0a, 0x10, 0x50, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, - 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, + 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x41, 0x73, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x6f, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x22, 0x25, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x79, 0x2d, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, + 0x43, 0x12, 0x80, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, + 0x23, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x22, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, + 0x52, 0x42, 0x41, 0x43, 0x12, 0x8f, 0x01, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, + 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, + 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x64, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x64, 0x2d, 0x61, + 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, + 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0xa4, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, + 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x3a, 0x01, 0x2a, 0x92, 0x41, 0x06, 0x0a, 0x04, 0x52, 0x42, 0x41, 0x43, 0x12, 0x98, 0x01, + 0x0a, 0x10, 0x50, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x74, 0x79, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, - 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x3a, - 0x01, 0x2a, 0x92, 0x41, 0x07, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0xbb, 0x01, 0x0a, - 0x19, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, - 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x34, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, - 0x79, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x92, 0x41, 0x0a, - 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0xda, 0x07, 0x5a, 0x33, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, - 0x76, 0x31, 0x92, 0x41, 0xa1, 0x07, 0x12, 0x95, 0x06, 0x0a, 0x15, 0x44, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x41, 0x50, 0x49, 0x20, 0x28, 0x42, 0x65, 0x74, 0x61, 0x29, - 0x12, 0xf5, 0x04, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x68, 0x65, - 0x6c, 0x70, 0x73, 0x20, 0x64, 0x65, 0x65, 0x70, 0x20, 0x6c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x20, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x20, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x20, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x73, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x6c, - 0x79, 0x2c, 0x20, 0x65, 0x61, 0x73, 0x69, 0x6c, 0x79, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20, - 0x47, 0x50, 0x55, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x2e, 0x20, 0x44, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x64, 0x65, - 0x65, 0x70, 0x20, 0x6c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x20, 0x6f, - 0x6e, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, - 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x20, 0x61, - 0x74, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, - 0x20, 0x6e, 0x65, 0x65, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x72, - 0x79, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x20, 0x6f, - 0x72, 0x20, 0x77, 0x72, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x20, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x20, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x65, - 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, - 0x6e, 0x67, 0x2e, 0x0a, 0x0a, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x69, - 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x67, 0x61, 0x70, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, 0x6f, 0x6f, 0x6c, - 0x73, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x54, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x46, 0x6c, 0x6f, - 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x50, 0x79, 0x54, 0x6f, 0x72, 0x63, 0x68, 0x20, 0x2d, 0x2d, - 0x2d, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x67, 0x72, 0x65, - 0x61, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, - 0x72, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, - 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x47, 0x50, 0x55, 0x20, 0x2d, 0x2d, 0x2d, - 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, - 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x69, 0x73, 0x65, 0x20, 0x77, 0x68, - 0x65, 0x6e, 0x20, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x65, 0x65, 0x70, 0x20, 0x6c, 0x65, - 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2c, - 0x20, 0x61, 0x73, 0x20, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x65, - 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65, 0x20, - 0x69, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2e, 0x22, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x41, 0x49, 0x12, 0x16, 0x68, 0x74, 0x74, 0x70, 0x73, - 0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x69, - 0x2f, 0x1a, 0x17, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x40, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x69, 0x2a, 0x3d, 0x0a, 0x0a, 0x41, 0x70, - 0x61, 0x63, 0x68, 0x65, 0x20, 0x32, 0x2e, 0x30, 0x12, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, - 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x2f, - 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, - 0x2d, 0x32, 0x2e, 0x30, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x2a, 0x02, - 0x01, 0x02, 0x5a, 0x4a, 0x0a, 0x48, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x39, 0x08, 0x02, 0x12, 0x24, 0x42, 0x65, 0x61, 0x72, 0x65, 0x72, 0x20, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x1a, 0x0d, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x02, 0x62, 0x11, - 0x0a, 0x0f, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x00, 0x72, 0x24, 0x0a, 0x1b, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, - 0x41, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x05, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, + 0x72, 0x73, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x3a, 0x01, 0x2a, 0x92, 0x41, + 0x07, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0xbb, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x64, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, + 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0xda, 0x07, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, + 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x76, 0x31, 0x92, 0x41, + 0xa1, 0x07, 0x12, 0x95, 0x06, 0x0a, 0x15, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x20, 0x41, 0x50, 0x49, 0x20, 0x28, 0x42, 0x65, 0x74, 0x61, 0x29, 0x12, 0xf5, 0x04, 0x44, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x73, 0x20, + 0x64, 0x65, 0x65, 0x70, 0x20, 0x6c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x20, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, + 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x6c, 0x79, 0x2c, 0x20, 0x65, + 0x61, 0x73, 0x69, 0x6c, 0x79, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20, 0x47, 0x50, 0x55, 0x20, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, + 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x61, + 0x62, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x2e, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x64, 0x65, 0x65, 0x70, 0x20, 0x6c, + 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x65, 0x72, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x72, 0x61, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x20, 0x61, 0x74, 0x20, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6e, 0x65, 0x65, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x72, 0x79, 0x20, 0x61, 0x62, + 0x6f, 0x75, 0x74, 0x20, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x77, 0x72, + 0x69, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x20, 0x63, 0x6f, 0x64, + 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x74, 0x61, 0x73, + 0x6b, 0x73, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x74, 0x6f, + 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x0a, + 0x0a, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x6f, + 0x66, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, + 0x61, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x61, 0x70, 0x20, + 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x54, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x46, 0x6c, 0x6f, 0x77, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x50, 0x79, 0x54, 0x6f, 0x72, 0x63, 0x68, 0x20, 0x2d, 0x2d, 0x2d, 0x20, 0x77, 0x68, + 0x69, 0x63, 0x68, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x47, 0x50, 0x55, 0x20, 0x2d, 0x2d, 0x2d, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x69, 0x73, 0x65, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x64, + 0x6f, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x65, 0x65, 0x70, 0x20, 0x6c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x20, 0x61, 0x74, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2c, 0x20, 0x61, 0x73, 0x20, + 0x74, 0x65, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x65, 0x74, 0x73, 0x20, 0x61, + 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x2e, 0x22, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x20, 0x41, 0x49, 0x12, 0x16, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x69, 0x2f, 0x1a, 0x17, 0x63, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x40, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x69, 0x2a, 0x3d, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, + 0x20, 0x32, 0x2e, 0x30, 0x12, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63, 0x65, + 0x6e, 0x73, 0x65, 0x73, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x2d, 0x32, 0x2e, 0x30, + 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x2a, 0x02, 0x01, 0x02, 0x5a, 0x4a, + 0x0a, 0x48, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, + 0x39, 0x08, 0x02, 0x12, 0x24, 0x42, 0x65, 0x61, 0x72, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x1a, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x02, 0x62, 0x11, 0x0a, 0x0f, 0x0a, 0x0b, + 0x42, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x00, 0x72, 0x24, 0x0a, + 0x1b, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x41, 0x49, 0x20, 0x44, + 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x05, 0x2f, 0x64, + 0x6f, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_determined_api_v1_api_proto_goTypes = []interface{}{ @@ -2180,238 +2157,234 @@ var file_determined_api_v1_api_proto_goTypes = []interface{}{ (*GetSearcherEventsRequest)(nil), // 129: determined.api.v1.GetSearcherEventsRequest (*PostSearcherOperationsRequest)(nil), // 130: determined.api.v1.PostSearcherOperationsRequest (*MetricNamesRequest)(nil), // 131: determined.api.v1.MetricNamesRequest - (*ExpCompareMetricNamesRequest)(nil), // 132: determined.api.v1.ExpCompareMetricNamesRequest - (*MetricBatchesRequest)(nil), // 133: determined.api.v1.MetricBatchesRequest - (*TrialsSnapshotRequest)(nil), // 134: determined.api.v1.TrialsSnapshotRequest - (*TrialsSampleRequest)(nil), // 135: determined.api.v1.TrialsSampleRequest - (*ExpCompareTrialsSampleRequest)(nil), // 136: determined.api.v1.ExpCompareTrialsSampleRequest - (*GetResourcePoolsRequest)(nil), // 137: determined.api.v1.GetResourcePoolsRequest - (*ComputeHPImportanceRequest)(nil), // 138: determined.api.v1.ComputeHPImportanceRequest - (*GetHPImportanceRequest)(nil), // 139: determined.api.v1.GetHPImportanceRequest - (*ResourceAllocationRawRequest)(nil), // 140: determined.api.v1.ResourceAllocationRawRequest - (*ResourceAllocationAggregatedRequest)(nil), // 141: determined.api.v1.ResourceAllocationAggregatedRequest - (*GetWorkspaceRequest)(nil), // 142: determined.api.v1.GetWorkspaceRequest - (*GetWorkspaceProjectsRequest)(nil), // 143: determined.api.v1.GetWorkspaceProjectsRequest - (*GetWorkspacesRequest)(nil), // 144: determined.api.v1.GetWorkspacesRequest - (*PostWorkspaceRequest)(nil), // 145: determined.api.v1.PostWorkspaceRequest - (*PatchWorkspaceRequest)(nil), // 146: determined.api.v1.PatchWorkspaceRequest - (*DeleteWorkspaceRequest)(nil), // 147: determined.api.v1.DeleteWorkspaceRequest - (*ArchiveWorkspaceRequest)(nil), // 148: determined.api.v1.ArchiveWorkspaceRequest - (*UnarchiveWorkspaceRequest)(nil), // 149: determined.api.v1.UnarchiveWorkspaceRequest - (*PinWorkspaceRequest)(nil), // 150: determined.api.v1.PinWorkspaceRequest - (*UnpinWorkspaceRequest)(nil), // 151: determined.api.v1.UnpinWorkspaceRequest - (*GetProjectRequest)(nil), // 152: determined.api.v1.GetProjectRequest - (*PostProjectRequest)(nil), // 153: determined.api.v1.PostProjectRequest - (*AddProjectNoteRequest)(nil), // 154: determined.api.v1.AddProjectNoteRequest - (*PutProjectNotesRequest)(nil), // 155: determined.api.v1.PutProjectNotesRequest - (*PatchProjectRequest)(nil), // 156: determined.api.v1.PatchProjectRequest - (*DeleteProjectRequest)(nil), // 157: determined.api.v1.DeleteProjectRequest - (*ArchiveProjectRequest)(nil), // 158: determined.api.v1.ArchiveProjectRequest - (*UnarchiveProjectRequest)(nil), // 159: determined.api.v1.UnarchiveProjectRequest - (*MoveProjectRequest)(nil), // 160: determined.api.v1.MoveProjectRequest - (*MoveExperimentRequest)(nil), // 161: determined.api.v1.MoveExperimentRequest - (*GetWebhooksRequest)(nil), // 162: determined.api.v1.GetWebhooksRequest - (*PostWebhookRequest)(nil), // 163: determined.api.v1.PostWebhookRequest - (*DeleteWebhookRequest)(nil), // 164: determined.api.v1.DeleteWebhookRequest - (*TestWebhookRequest)(nil), // 165: determined.api.v1.TestWebhookRequest - (*GetGroupRequest)(nil), // 166: determined.api.v1.GetGroupRequest - (*GetGroupsRequest)(nil), // 167: determined.api.v1.GetGroupsRequest - (*CreateGroupRequest)(nil), // 168: determined.api.v1.CreateGroupRequest - (*UpdateGroupRequest)(nil), // 169: determined.api.v1.UpdateGroupRequest - (*DeleteGroupRequest)(nil), // 170: determined.api.v1.DeleteGroupRequest - (*GetPermissionsSummaryRequest)(nil), // 171: determined.api.v1.GetPermissionsSummaryRequest - (*GetGroupsAndUsersAssignedToWorkspaceRequest)(nil), // 172: determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceRequest - (*GetRolesByIDRequest)(nil), // 173: determined.api.v1.GetRolesByIDRequest - (*GetRolesAssignedToUserRequest)(nil), // 174: determined.api.v1.GetRolesAssignedToUserRequest - (*GetRolesAssignedToGroupRequest)(nil), // 175: determined.api.v1.GetRolesAssignedToGroupRequest - (*SearchRolesAssignableToScopeRequest)(nil), // 176: determined.api.v1.SearchRolesAssignableToScopeRequest - (*ListRolesRequest)(nil), // 177: determined.api.v1.ListRolesRequest - (*AssignRolesRequest)(nil), // 178: determined.api.v1.AssignRolesRequest - (*RemoveAssignmentsRequest)(nil), // 179: determined.api.v1.RemoveAssignmentsRequest - (*PostUserActivityRequest)(nil), // 180: determined.api.v1.PostUserActivityRequest - (*GetProjectsByUserActivityRequest)(nil), // 181: determined.api.v1.GetProjectsByUserActivityRequest - (*LoginResponse)(nil), // 182: determined.api.v1.LoginResponse - (*CurrentUserResponse)(nil), // 183: determined.api.v1.CurrentUserResponse - (*LogoutResponse)(nil), // 184: determined.api.v1.LogoutResponse - (*GetUsersResponse)(nil), // 185: determined.api.v1.GetUsersResponse - (*GetUserSettingResponse)(nil), // 186: determined.api.v1.GetUserSettingResponse - (*ResetUserSettingResponse)(nil), // 187: determined.api.v1.ResetUserSettingResponse - (*PostUserSettingResponse)(nil), // 188: determined.api.v1.PostUserSettingResponse - (*GetUserResponse)(nil), // 189: determined.api.v1.GetUserResponse - (*GetUserByUsernameResponse)(nil), // 190: determined.api.v1.GetUserByUsernameResponse - (*GetMeResponse)(nil), // 191: determined.api.v1.GetMeResponse - (*PostUserResponse)(nil), // 192: determined.api.v1.PostUserResponse - (*SetUserPasswordResponse)(nil), // 193: determined.api.v1.SetUserPasswordResponse - (*PatchUserResponse)(nil), // 194: determined.api.v1.PatchUserResponse - (*GetTelemetryResponse)(nil), // 195: determined.api.v1.GetTelemetryResponse - (*GetMasterResponse)(nil), // 196: determined.api.v1.GetMasterResponse - (*GetMasterConfigResponse)(nil), // 197: determined.api.v1.GetMasterConfigResponse - (*MasterLogsResponse)(nil), // 198: determined.api.v1.MasterLogsResponse - (*GetAgentsResponse)(nil), // 199: determined.api.v1.GetAgentsResponse - (*GetAgentResponse)(nil), // 200: determined.api.v1.GetAgentResponse - (*GetSlotsResponse)(nil), // 201: determined.api.v1.GetSlotsResponse - (*GetSlotResponse)(nil), // 202: determined.api.v1.GetSlotResponse - (*EnableAgentResponse)(nil), // 203: determined.api.v1.EnableAgentResponse - (*DisableAgentResponse)(nil), // 204: determined.api.v1.DisableAgentResponse - (*EnableSlotResponse)(nil), // 205: determined.api.v1.EnableSlotResponse - (*DisableSlotResponse)(nil), // 206: determined.api.v1.DisableSlotResponse - (*CreateExperimentResponse)(nil), // 207: determined.api.v1.CreateExperimentResponse - (*GetExperimentResponse)(nil), // 208: determined.api.v1.GetExperimentResponse - (*GetExperimentsResponse)(nil), // 209: determined.api.v1.GetExperimentsResponse - (*GetModelDefResponse)(nil), // 210: determined.api.v1.GetModelDefResponse - (*GetModelDefTreeResponse)(nil), // 211: determined.api.v1.GetModelDefTreeResponse - (*GetModelDefFileResponse)(nil), // 212: determined.api.v1.GetModelDefFileResponse - (*GetExperimentLabelsResponse)(nil), // 213: determined.api.v1.GetExperimentLabelsResponse - (*GetExperimentValidationHistoryResponse)(nil), // 214: determined.api.v1.GetExperimentValidationHistoryResponse - (*ActivateExperimentResponse)(nil), // 215: determined.api.v1.ActivateExperimentResponse - (*PauseExperimentResponse)(nil), // 216: determined.api.v1.PauseExperimentResponse - (*CancelExperimentResponse)(nil), // 217: determined.api.v1.CancelExperimentResponse - (*KillExperimentResponse)(nil), // 218: determined.api.v1.KillExperimentResponse - (*ArchiveExperimentResponse)(nil), // 219: determined.api.v1.ArchiveExperimentResponse - (*UnarchiveExperimentResponse)(nil), // 220: determined.api.v1.UnarchiveExperimentResponse - (*PatchExperimentResponse)(nil), // 221: determined.api.v1.PatchExperimentResponse - (*DeleteExperimentResponse)(nil), // 222: determined.api.v1.DeleteExperimentResponse - (*GetBestSearcherValidationMetricResponse)(nil), // 223: determined.api.v1.GetBestSearcherValidationMetricResponse - (*GetExperimentCheckpointsResponse)(nil), // 224: determined.api.v1.GetExperimentCheckpointsResponse - (*PreviewHPSearchResponse)(nil), // 225: determined.api.v1.PreviewHPSearchResponse - (*GetExperimentTrialsResponse)(nil), // 226: determined.api.v1.GetExperimentTrialsResponse - (*CompareTrialsResponse)(nil), // 227: determined.api.v1.CompareTrialsResponse - (*QueryTrialsResponse)(nil), // 228: determined.api.v1.QueryTrialsResponse - (*UpdateTrialTagsResponse)(nil), // 229: determined.api.v1.UpdateTrialTagsResponse - (*GetTrialsCollectionsResponse)(nil), // 230: determined.api.v1.GetTrialsCollectionsResponse - (*CreateTrialsCollectionResponse)(nil), // 231: determined.api.v1.CreateTrialsCollectionResponse - (*PatchTrialsCollectionResponse)(nil), // 232: determined.api.v1.PatchTrialsCollectionResponse - (*DeleteTrialsCollectionResponse)(nil), // 233: determined.api.v1.DeleteTrialsCollectionResponse - (*GetTrialResponse)(nil), // 234: determined.api.v1.GetTrialResponse - (*GetTrialWorkloadsResponse)(nil), // 235: determined.api.v1.GetTrialWorkloadsResponse - (*TrialLogsResponse)(nil), // 236: determined.api.v1.TrialLogsResponse - (*TrialLogsFieldsResponse)(nil), // 237: determined.api.v1.TrialLogsFieldsResponse - (*SummarizeTrialResponse)(nil), // 238: determined.api.v1.SummarizeTrialResponse - (*AllocationReadyResponse)(nil), // 239: determined.api.v1.AllocationReadyResponse - (*AllocationWaitingResponse)(nil), // 240: determined.api.v1.AllocationWaitingResponse - (*TaskLogsResponse)(nil), // 241: determined.api.v1.TaskLogsResponse - (*TaskLogsFieldsResponse)(nil), // 242: determined.api.v1.TaskLogsFieldsResponse - (*GetTrialProfilerMetricsResponse)(nil), // 243: determined.api.v1.GetTrialProfilerMetricsResponse - (*GetTrialProfilerAvailableSeriesResponse)(nil), // 244: determined.api.v1.GetTrialProfilerAvailableSeriesResponse - (*PostTrialProfilerMetricsBatchResponse)(nil), // 245: determined.api.v1.PostTrialProfilerMetricsBatchResponse - (*KillTrialResponse)(nil), // 246: determined.api.v1.KillTrialResponse - (*GetTrialCheckpointsResponse)(nil), // 247: determined.api.v1.GetTrialCheckpointsResponse - (*AllocationPreemptionSignalResponse)(nil), // 248: determined.api.v1.AllocationPreemptionSignalResponse - (*AllocationPendingPreemptionSignalResponse)(nil), // 249: determined.api.v1.AllocationPendingPreemptionSignalResponse - (*AckAllocationPreemptionSignalResponse)(nil), // 250: determined.api.v1.AckAllocationPreemptionSignalResponse - (*MarkAllocationResourcesDaemonResponse)(nil), // 251: determined.api.v1.MarkAllocationResourcesDaemonResponse - (*AllocationRendezvousInfoResponse)(nil), // 252: determined.api.v1.AllocationRendezvousInfoResponse - (*PostAllocationProxyAddressResponse)(nil), // 253: determined.api.v1.PostAllocationProxyAddressResponse - (*AllocationAllGatherResponse)(nil), // 254: determined.api.v1.AllocationAllGatherResponse - (*NotifyContainerRunningResponse)(nil), // 255: determined.api.v1.NotifyContainerRunningResponse - (*GetCurrentTrialSearcherOperationResponse)(nil), // 256: determined.api.v1.GetCurrentTrialSearcherOperationResponse - (*CompleteTrialSearcherValidationResponse)(nil), // 257: determined.api.v1.CompleteTrialSearcherValidationResponse - (*ReportTrialSearcherEarlyExitResponse)(nil), // 258: determined.api.v1.ReportTrialSearcherEarlyExitResponse - (*ReportTrialProgressResponse)(nil), // 259: determined.api.v1.ReportTrialProgressResponse - (*PostTrialRunnerMetadataResponse)(nil), // 260: determined.api.v1.PostTrialRunnerMetadataResponse - (*ReportTrialTrainingMetricsResponse)(nil), // 261: determined.api.v1.ReportTrialTrainingMetricsResponse - (*ReportTrialValidationMetricsResponse)(nil), // 262: determined.api.v1.ReportTrialValidationMetricsResponse - (*ReportCheckpointResponse)(nil), // 263: determined.api.v1.ReportCheckpointResponse - (*GetJobsResponse)(nil), // 264: determined.api.v1.GetJobsResponse - (*GetJobQueueStatsResponse)(nil), // 265: determined.api.v1.GetJobQueueStatsResponse - (*UpdateJobQueueResponse)(nil), // 266: determined.api.v1.UpdateJobQueueResponse - (*GetTemplatesResponse)(nil), // 267: determined.api.v1.GetTemplatesResponse - (*GetTemplateResponse)(nil), // 268: determined.api.v1.GetTemplateResponse - (*PutTemplateResponse)(nil), // 269: determined.api.v1.PutTemplateResponse - (*DeleteTemplateResponse)(nil), // 270: determined.api.v1.DeleteTemplateResponse - (*GetNotebooksResponse)(nil), // 271: determined.api.v1.GetNotebooksResponse - (*GetNotebookResponse)(nil), // 272: determined.api.v1.GetNotebookResponse - (*IdleNotebookResponse)(nil), // 273: determined.api.v1.IdleNotebookResponse - (*KillNotebookResponse)(nil), // 274: determined.api.v1.KillNotebookResponse - (*SetNotebookPriorityResponse)(nil), // 275: determined.api.v1.SetNotebookPriorityResponse - (*LaunchNotebookResponse)(nil), // 276: determined.api.v1.LaunchNotebookResponse - (*GetShellsResponse)(nil), // 277: determined.api.v1.GetShellsResponse - (*GetShellResponse)(nil), // 278: determined.api.v1.GetShellResponse - (*KillShellResponse)(nil), // 279: determined.api.v1.KillShellResponse - (*SetShellPriorityResponse)(nil), // 280: determined.api.v1.SetShellPriorityResponse - (*LaunchShellResponse)(nil), // 281: determined.api.v1.LaunchShellResponse - (*GetCommandsResponse)(nil), // 282: determined.api.v1.GetCommandsResponse - (*GetCommandResponse)(nil), // 283: determined.api.v1.GetCommandResponse - (*KillCommandResponse)(nil), // 284: determined.api.v1.KillCommandResponse - (*SetCommandPriorityResponse)(nil), // 285: determined.api.v1.SetCommandPriorityResponse - (*LaunchCommandResponse)(nil), // 286: determined.api.v1.LaunchCommandResponse - (*GetTensorboardsResponse)(nil), // 287: determined.api.v1.GetTensorboardsResponse - (*GetTensorboardResponse)(nil), // 288: determined.api.v1.GetTensorboardResponse - (*KillTensorboardResponse)(nil), // 289: determined.api.v1.KillTensorboardResponse - (*SetTensorboardPriorityResponse)(nil), // 290: determined.api.v1.SetTensorboardPriorityResponse - (*LaunchTensorboardResponse)(nil), // 291: determined.api.v1.LaunchTensorboardResponse - (*GetActiveTasksCountResponse)(nil), // 292: determined.api.v1.GetActiveTasksCountResponse - (*GetTaskResponse)(nil), // 293: determined.api.v1.GetTaskResponse - (*GetModelResponse)(nil), // 294: determined.api.v1.GetModelResponse - (*PostModelResponse)(nil), // 295: determined.api.v1.PostModelResponse - (*PatchModelResponse)(nil), // 296: determined.api.v1.PatchModelResponse - (*ArchiveModelResponse)(nil), // 297: determined.api.v1.ArchiveModelResponse - (*UnarchiveModelResponse)(nil), // 298: determined.api.v1.UnarchiveModelResponse - (*MoveModelResponse)(nil), // 299: determined.api.v1.MoveModelResponse - (*DeleteModelResponse)(nil), // 300: determined.api.v1.DeleteModelResponse - (*GetModelsResponse)(nil), // 301: determined.api.v1.GetModelsResponse - (*GetModelLabelsResponse)(nil), // 302: determined.api.v1.GetModelLabelsResponse - (*GetModelVersionResponse)(nil), // 303: determined.api.v1.GetModelVersionResponse - (*GetModelVersionsResponse)(nil), // 304: determined.api.v1.GetModelVersionsResponse - (*PostModelVersionResponse)(nil), // 305: determined.api.v1.PostModelVersionResponse - (*PatchModelVersionResponse)(nil), // 306: determined.api.v1.PatchModelVersionResponse - (*DeleteModelVersionResponse)(nil), // 307: determined.api.v1.DeleteModelVersionResponse - (*GetCheckpointResponse)(nil), // 308: determined.api.v1.GetCheckpointResponse - (*PostCheckpointMetadataResponse)(nil), // 309: determined.api.v1.PostCheckpointMetadataResponse - (*DeleteCheckpointsResponse)(nil), // 310: determined.api.v1.DeleteCheckpointsResponse - (*GetSearcherEventsResponse)(nil), // 311: determined.api.v1.GetSearcherEventsResponse - (*PostSearcherOperationsResponse)(nil), // 312: determined.api.v1.PostSearcherOperationsResponse - (*MetricNamesResponse)(nil), // 313: determined.api.v1.MetricNamesResponse - (*ExpCompareMetricNamesResponse)(nil), // 314: determined.api.v1.ExpCompareMetricNamesResponse - (*MetricBatchesResponse)(nil), // 315: determined.api.v1.MetricBatchesResponse - (*TrialsSnapshotResponse)(nil), // 316: determined.api.v1.TrialsSnapshotResponse - (*TrialsSampleResponse)(nil), // 317: determined.api.v1.TrialsSampleResponse - (*ExpCompareTrialsSampleResponse)(nil), // 318: determined.api.v1.ExpCompareTrialsSampleResponse - (*GetResourcePoolsResponse)(nil), // 319: determined.api.v1.GetResourcePoolsResponse - (*ComputeHPImportanceResponse)(nil), // 320: determined.api.v1.ComputeHPImportanceResponse - (*GetHPImportanceResponse)(nil), // 321: determined.api.v1.GetHPImportanceResponse - (*ResourceAllocationRawResponse)(nil), // 322: determined.api.v1.ResourceAllocationRawResponse - (*ResourceAllocationAggregatedResponse)(nil), // 323: determined.api.v1.ResourceAllocationAggregatedResponse - (*GetWorkspaceResponse)(nil), // 324: determined.api.v1.GetWorkspaceResponse - (*GetWorkspaceProjectsResponse)(nil), // 325: determined.api.v1.GetWorkspaceProjectsResponse - (*GetWorkspacesResponse)(nil), // 326: determined.api.v1.GetWorkspacesResponse - (*PostWorkspaceResponse)(nil), // 327: determined.api.v1.PostWorkspaceResponse - (*PatchWorkspaceResponse)(nil), // 328: determined.api.v1.PatchWorkspaceResponse - (*DeleteWorkspaceResponse)(nil), // 329: determined.api.v1.DeleteWorkspaceResponse - (*ArchiveWorkspaceResponse)(nil), // 330: determined.api.v1.ArchiveWorkspaceResponse - (*UnarchiveWorkspaceResponse)(nil), // 331: determined.api.v1.UnarchiveWorkspaceResponse - (*PinWorkspaceResponse)(nil), // 332: determined.api.v1.PinWorkspaceResponse - (*UnpinWorkspaceResponse)(nil), // 333: determined.api.v1.UnpinWorkspaceResponse - (*GetProjectResponse)(nil), // 334: determined.api.v1.GetProjectResponse - (*PostProjectResponse)(nil), // 335: determined.api.v1.PostProjectResponse - (*AddProjectNoteResponse)(nil), // 336: determined.api.v1.AddProjectNoteResponse - (*PutProjectNotesResponse)(nil), // 337: determined.api.v1.PutProjectNotesResponse - (*PatchProjectResponse)(nil), // 338: determined.api.v1.PatchProjectResponse - (*DeleteProjectResponse)(nil), // 339: determined.api.v1.DeleteProjectResponse - (*ArchiveProjectResponse)(nil), // 340: determined.api.v1.ArchiveProjectResponse - (*UnarchiveProjectResponse)(nil), // 341: determined.api.v1.UnarchiveProjectResponse - (*MoveProjectResponse)(nil), // 342: determined.api.v1.MoveProjectResponse - (*MoveExperimentResponse)(nil), // 343: determined.api.v1.MoveExperimentResponse - (*GetWebhooksResponse)(nil), // 344: determined.api.v1.GetWebhooksResponse - (*PostWebhookResponse)(nil), // 345: determined.api.v1.PostWebhookResponse - (*DeleteWebhookResponse)(nil), // 346: determined.api.v1.DeleteWebhookResponse - (*TestWebhookResponse)(nil), // 347: determined.api.v1.TestWebhookResponse - (*GetGroupResponse)(nil), // 348: determined.api.v1.GetGroupResponse - (*GetGroupsResponse)(nil), // 349: determined.api.v1.GetGroupsResponse - (*CreateGroupResponse)(nil), // 350: determined.api.v1.CreateGroupResponse - (*UpdateGroupResponse)(nil), // 351: determined.api.v1.UpdateGroupResponse - (*DeleteGroupResponse)(nil), // 352: determined.api.v1.DeleteGroupResponse - (*GetPermissionsSummaryResponse)(nil), // 353: determined.api.v1.GetPermissionsSummaryResponse - (*GetGroupsAndUsersAssignedToWorkspaceResponse)(nil), // 354: determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceResponse - (*GetRolesByIDResponse)(nil), // 355: determined.api.v1.GetRolesByIDResponse - (*GetRolesAssignedToUserResponse)(nil), // 356: determined.api.v1.GetRolesAssignedToUserResponse - (*GetRolesAssignedToGroupResponse)(nil), // 357: determined.api.v1.GetRolesAssignedToGroupResponse - (*SearchRolesAssignableToScopeResponse)(nil), // 358: determined.api.v1.SearchRolesAssignableToScopeResponse - (*ListRolesResponse)(nil), // 359: determined.api.v1.ListRolesResponse - (*AssignRolesResponse)(nil), // 360: determined.api.v1.AssignRolesResponse - (*RemoveAssignmentsResponse)(nil), // 361: determined.api.v1.RemoveAssignmentsResponse - (*PostUserActivityResponse)(nil), // 362: determined.api.v1.PostUserActivityResponse - (*GetProjectsByUserActivityResponse)(nil), // 363: determined.api.v1.GetProjectsByUserActivityResponse + (*MetricBatchesRequest)(nil), // 132: determined.api.v1.MetricBatchesRequest + (*TrialsSnapshotRequest)(nil), // 133: determined.api.v1.TrialsSnapshotRequest + (*TrialsSampleRequest)(nil), // 134: determined.api.v1.TrialsSampleRequest + (*GetResourcePoolsRequest)(nil), // 135: determined.api.v1.GetResourcePoolsRequest + (*ComputeHPImportanceRequest)(nil), // 136: determined.api.v1.ComputeHPImportanceRequest + (*GetHPImportanceRequest)(nil), // 137: determined.api.v1.GetHPImportanceRequest + (*ResourceAllocationRawRequest)(nil), // 138: determined.api.v1.ResourceAllocationRawRequest + (*ResourceAllocationAggregatedRequest)(nil), // 139: determined.api.v1.ResourceAllocationAggregatedRequest + (*GetWorkspaceRequest)(nil), // 140: determined.api.v1.GetWorkspaceRequest + (*GetWorkspaceProjectsRequest)(nil), // 141: determined.api.v1.GetWorkspaceProjectsRequest + (*GetWorkspacesRequest)(nil), // 142: determined.api.v1.GetWorkspacesRequest + (*PostWorkspaceRequest)(nil), // 143: determined.api.v1.PostWorkspaceRequest + (*PatchWorkspaceRequest)(nil), // 144: determined.api.v1.PatchWorkspaceRequest + (*DeleteWorkspaceRequest)(nil), // 145: determined.api.v1.DeleteWorkspaceRequest + (*ArchiveWorkspaceRequest)(nil), // 146: determined.api.v1.ArchiveWorkspaceRequest + (*UnarchiveWorkspaceRequest)(nil), // 147: determined.api.v1.UnarchiveWorkspaceRequest + (*PinWorkspaceRequest)(nil), // 148: determined.api.v1.PinWorkspaceRequest + (*UnpinWorkspaceRequest)(nil), // 149: determined.api.v1.UnpinWorkspaceRequest + (*GetProjectRequest)(nil), // 150: determined.api.v1.GetProjectRequest + (*PostProjectRequest)(nil), // 151: determined.api.v1.PostProjectRequest + (*AddProjectNoteRequest)(nil), // 152: determined.api.v1.AddProjectNoteRequest + (*PutProjectNotesRequest)(nil), // 153: determined.api.v1.PutProjectNotesRequest + (*PatchProjectRequest)(nil), // 154: determined.api.v1.PatchProjectRequest + (*DeleteProjectRequest)(nil), // 155: determined.api.v1.DeleteProjectRequest + (*ArchiveProjectRequest)(nil), // 156: determined.api.v1.ArchiveProjectRequest + (*UnarchiveProjectRequest)(nil), // 157: determined.api.v1.UnarchiveProjectRequest + (*MoveProjectRequest)(nil), // 158: determined.api.v1.MoveProjectRequest + (*MoveExperimentRequest)(nil), // 159: determined.api.v1.MoveExperimentRequest + (*GetWebhooksRequest)(nil), // 160: determined.api.v1.GetWebhooksRequest + (*PostWebhookRequest)(nil), // 161: determined.api.v1.PostWebhookRequest + (*DeleteWebhookRequest)(nil), // 162: determined.api.v1.DeleteWebhookRequest + (*TestWebhookRequest)(nil), // 163: determined.api.v1.TestWebhookRequest + (*GetGroupRequest)(nil), // 164: determined.api.v1.GetGroupRequest + (*GetGroupsRequest)(nil), // 165: determined.api.v1.GetGroupsRequest + (*CreateGroupRequest)(nil), // 166: determined.api.v1.CreateGroupRequest + (*UpdateGroupRequest)(nil), // 167: determined.api.v1.UpdateGroupRequest + (*DeleteGroupRequest)(nil), // 168: determined.api.v1.DeleteGroupRequest + (*GetPermissionsSummaryRequest)(nil), // 169: determined.api.v1.GetPermissionsSummaryRequest + (*GetGroupsAndUsersAssignedToWorkspaceRequest)(nil), // 170: determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceRequest + (*GetRolesByIDRequest)(nil), // 171: determined.api.v1.GetRolesByIDRequest + (*GetRolesAssignedToUserRequest)(nil), // 172: determined.api.v1.GetRolesAssignedToUserRequest + (*GetRolesAssignedToGroupRequest)(nil), // 173: determined.api.v1.GetRolesAssignedToGroupRequest + (*SearchRolesAssignableToScopeRequest)(nil), // 174: determined.api.v1.SearchRolesAssignableToScopeRequest + (*ListRolesRequest)(nil), // 175: determined.api.v1.ListRolesRequest + (*AssignRolesRequest)(nil), // 176: determined.api.v1.AssignRolesRequest + (*RemoveAssignmentsRequest)(nil), // 177: determined.api.v1.RemoveAssignmentsRequest + (*PostUserActivityRequest)(nil), // 178: determined.api.v1.PostUserActivityRequest + (*GetProjectsByUserActivityRequest)(nil), // 179: determined.api.v1.GetProjectsByUserActivityRequest + (*LoginResponse)(nil), // 180: determined.api.v1.LoginResponse + (*CurrentUserResponse)(nil), // 181: determined.api.v1.CurrentUserResponse + (*LogoutResponse)(nil), // 182: determined.api.v1.LogoutResponse + (*GetUsersResponse)(nil), // 183: determined.api.v1.GetUsersResponse + (*GetUserSettingResponse)(nil), // 184: determined.api.v1.GetUserSettingResponse + (*ResetUserSettingResponse)(nil), // 185: determined.api.v1.ResetUserSettingResponse + (*PostUserSettingResponse)(nil), // 186: determined.api.v1.PostUserSettingResponse + (*GetUserResponse)(nil), // 187: determined.api.v1.GetUserResponse + (*GetUserByUsernameResponse)(nil), // 188: determined.api.v1.GetUserByUsernameResponse + (*GetMeResponse)(nil), // 189: determined.api.v1.GetMeResponse + (*PostUserResponse)(nil), // 190: determined.api.v1.PostUserResponse + (*SetUserPasswordResponse)(nil), // 191: determined.api.v1.SetUserPasswordResponse + (*PatchUserResponse)(nil), // 192: determined.api.v1.PatchUserResponse + (*GetTelemetryResponse)(nil), // 193: determined.api.v1.GetTelemetryResponse + (*GetMasterResponse)(nil), // 194: determined.api.v1.GetMasterResponse + (*GetMasterConfigResponse)(nil), // 195: determined.api.v1.GetMasterConfigResponse + (*MasterLogsResponse)(nil), // 196: determined.api.v1.MasterLogsResponse + (*GetAgentsResponse)(nil), // 197: determined.api.v1.GetAgentsResponse + (*GetAgentResponse)(nil), // 198: determined.api.v1.GetAgentResponse + (*GetSlotsResponse)(nil), // 199: determined.api.v1.GetSlotsResponse + (*GetSlotResponse)(nil), // 200: determined.api.v1.GetSlotResponse + (*EnableAgentResponse)(nil), // 201: determined.api.v1.EnableAgentResponse + (*DisableAgentResponse)(nil), // 202: determined.api.v1.DisableAgentResponse + (*EnableSlotResponse)(nil), // 203: determined.api.v1.EnableSlotResponse + (*DisableSlotResponse)(nil), // 204: determined.api.v1.DisableSlotResponse + (*CreateExperimentResponse)(nil), // 205: determined.api.v1.CreateExperimentResponse + (*GetExperimentResponse)(nil), // 206: determined.api.v1.GetExperimentResponse + (*GetExperimentsResponse)(nil), // 207: determined.api.v1.GetExperimentsResponse + (*GetModelDefResponse)(nil), // 208: determined.api.v1.GetModelDefResponse + (*GetModelDefTreeResponse)(nil), // 209: determined.api.v1.GetModelDefTreeResponse + (*GetModelDefFileResponse)(nil), // 210: determined.api.v1.GetModelDefFileResponse + (*GetExperimentLabelsResponse)(nil), // 211: determined.api.v1.GetExperimentLabelsResponse + (*GetExperimentValidationHistoryResponse)(nil), // 212: determined.api.v1.GetExperimentValidationHistoryResponse + (*ActivateExperimentResponse)(nil), // 213: determined.api.v1.ActivateExperimentResponse + (*PauseExperimentResponse)(nil), // 214: determined.api.v1.PauseExperimentResponse + (*CancelExperimentResponse)(nil), // 215: determined.api.v1.CancelExperimentResponse + (*KillExperimentResponse)(nil), // 216: determined.api.v1.KillExperimentResponse + (*ArchiveExperimentResponse)(nil), // 217: determined.api.v1.ArchiveExperimentResponse + (*UnarchiveExperimentResponse)(nil), // 218: determined.api.v1.UnarchiveExperimentResponse + (*PatchExperimentResponse)(nil), // 219: determined.api.v1.PatchExperimentResponse + (*DeleteExperimentResponse)(nil), // 220: determined.api.v1.DeleteExperimentResponse + (*GetBestSearcherValidationMetricResponse)(nil), // 221: determined.api.v1.GetBestSearcherValidationMetricResponse + (*GetExperimentCheckpointsResponse)(nil), // 222: determined.api.v1.GetExperimentCheckpointsResponse + (*PreviewHPSearchResponse)(nil), // 223: determined.api.v1.PreviewHPSearchResponse + (*GetExperimentTrialsResponse)(nil), // 224: determined.api.v1.GetExperimentTrialsResponse + (*CompareTrialsResponse)(nil), // 225: determined.api.v1.CompareTrialsResponse + (*QueryTrialsResponse)(nil), // 226: determined.api.v1.QueryTrialsResponse + (*UpdateTrialTagsResponse)(nil), // 227: determined.api.v1.UpdateTrialTagsResponse + (*GetTrialsCollectionsResponse)(nil), // 228: determined.api.v1.GetTrialsCollectionsResponse + (*CreateTrialsCollectionResponse)(nil), // 229: determined.api.v1.CreateTrialsCollectionResponse + (*PatchTrialsCollectionResponse)(nil), // 230: determined.api.v1.PatchTrialsCollectionResponse + (*DeleteTrialsCollectionResponse)(nil), // 231: determined.api.v1.DeleteTrialsCollectionResponse + (*GetTrialResponse)(nil), // 232: determined.api.v1.GetTrialResponse + (*GetTrialWorkloadsResponse)(nil), // 233: determined.api.v1.GetTrialWorkloadsResponse + (*TrialLogsResponse)(nil), // 234: determined.api.v1.TrialLogsResponse + (*TrialLogsFieldsResponse)(nil), // 235: determined.api.v1.TrialLogsFieldsResponse + (*SummarizeTrialResponse)(nil), // 236: determined.api.v1.SummarizeTrialResponse + (*AllocationReadyResponse)(nil), // 237: determined.api.v1.AllocationReadyResponse + (*AllocationWaitingResponse)(nil), // 238: determined.api.v1.AllocationWaitingResponse + (*TaskLogsResponse)(nil), // 239: determined.api.v1.TaskLogsResponse + (*TaskLogsFieldsResponse)(nil), // 240: determined.api.v1.TaskLogsFieldsResponse + (*GetTrialProfilerMetricsResponse)(nil), // 241: determined.api.v1.GetTrialProfilerMetricsResponse + (*GetTrialProfilerAvailableSeriesResponse)(nil), // 242: determined.api.v1.GetTrialProfilerAvailableSeriesResponse + (*PostTrialProfilerMetricsBatchResponse)(nil), // 243: determined.api.v1.PostTrialProfilerMetricsBatchResponse + (*KillTrialResponse)(nil), // 244: determined.api.v1.KillTrialResponse + (*GetTrialCheckpointsResponse)(nil), // 245: determined.api.v1.GetTrialCheckpointsResponse + (*AllocationPreemptionSignalResponse)(nil), // 246: determined.api.v1.AllocationPreemptionSignalResponse + (*AllocationPendingPreemptionSignalResponse)(nil), // 247: determined.api.v1.AllocationPendingPreemptionSignalResponse + (*AckAllocationPreemptionSignalResponse)(nil), // 248: determined.api.v1.AckAllocationPreemptionSignalResponse + (*MarkAllocationResourcesDaemonResponse)(nil), // 249: determined.api.v1.MarkAllocationResourcesDaemonResponse + (*AllocationRendezvousInfoResponse)(nil), // 250: determined.api.v1.AllocationRendezvousInfoResponse + (*PostAllocationProxyAddressResponse)(nil), // 251: determined.api.v1.PostAllocationProxyAddressResponse + (*AllocationAllGatherResponse)(nil), // 252: determined.api.v1.AllocationAllGatherResponse + (*NotifyContainerRunningResponse)(nil), // 253: determined.api.v1.NotifyContainerRunningResponse + (*GetCurrentTrialSearcherOperationResponse)(nil), // 254: determined.api.v1.GetCurrentTrialSearcherOperationResponse + (*CompleteTrialSearcherValidationResponse)(nil), // 255: determined.api.v1.CompleteTrialSearcherValidationResponse + (*ReportTrialSearcherEarlyExitResponse)(nil), // 256: determined.api.v1.ReportTrialSearcherEarlyExitResponse + (*ReportTrialProgressResponse)(nil), // 257: determined.api.v1.ReportTrialProgressResponse + (*PostTrialRunnerMetadataResponse)(nil), // 258: determined.api.v1.PostTrialRunnerMetadataResponse + (*ReportTrialTrainingMetricsResponse)(nil), // 259: determined.api.v1.ReportTrialTrainingMetricsResponse + (*ReportTrialValidationMetricsResponse)(nil), // 260: determined.api.v1.ReportTrialValidationMetricsResponse + (*ReportCheckpointResponse)(nil), // 261: determined.api.v1.ReportCheckpointResponse + (*GetJobsResponse)(nil), // 262: determined.api.v1.GetJobsResponse + (*GetJobQueueStatsResponse)(nil), // 263: determined.api.v1.GetJobQueueStatsResponse + (*UpdateJobQueueResponse)(nil), // 264: determined.api.v1.UpdateJobQueueResponse + (*GetTemplatesResponse)(nil), // 265: determined.api.v1.GetTemplatesResponse + (*GetTemplateResponse)(nil), // 266: determined.api.v1.GetTemplateResponse + (*PutTemplateResponse)(nil), // 267: determined.api.v1.PutTemplateResponse + (*DeleteTemplateResponse)(nil), // 268: determined.api.v1.DeleteTemplateResponse + (*GetNotebooksResponse)(nil), // 269: determined.api.v1.GetNotebooksResponse + (*GetNotebookResponse)(nil), // 270: determined.api.v1.GetNotebookResponse + (*IdleNotebookResponse)(nil), // 271: determined.api.v1.IdleNotebookResponse + (*KillNotebookResponse)(nil), // 272: determined.api.v1.KillNotebookResponse + (*SetNotebookPriorityResponse)(nil), // 273: determined.api.v1.SetNotebookPriorityResponse + (*LaunchNotebookResponse)(nil), // 274: determined.api.v1.LaunchNotebookResponse + (*GetShellsResponse)(nil), // 275: determined.api.v1.GetShellsResponse + (*GetShellResponse)(nil), // 276: determined.api.v1.GetShellResponse + (*KillShellResponse)(nil), // 277: determined.api.v1.KillShellResponse + (*SetShellPriorityResponse)(nil), // 278: determined.api.v1.SetShellPriorityResponse + (*LaunchShellResponse)(nil), // 279: determined.api.v1.LaunchShellResponse + (*GetCommandsResponse)(nil), // 280: determined.api.v1.GetCommandsResponse + (*GetCommandResponse)(nil), // 281: determined.api.v1.GetCommandResponse + (*KillCommandResponse)(nil), // 282: determined.api.v1.KillCommandResponse + (*SetCommandPriorityResponse)(nil), // 283: determined.api.v1.SetCommandPriorityResponse + (*LaunchCommandResponse)(nil), // 284: determined.api.v1.LaunchCommandResponse + (*GetTensorboardsResponse)(nil), // 285: determined.api.v1.GetTensorboardsResponse + (*GetTensorboardResponse)(nil), // 286: determined.api.v1.GetTensorboardResponse + (*KillTensorboardResponse)(nil), // 287: determined.api.v1.KillTensorboardResponse + (*SetTensorboardPriorityResponse)(nil), // 288: determined.api.v1.SetTensorboardPriorityResponse + (*LaunchTensorboardResponse)(nil), // 289: determined.api.v1.LaunchTensorboardResponse + (*GetActiveTasksCountResponse)(nil), // 290: determined.api.v1.GetActiveTasksCountResponse + (*GetTaskResponse)(nil), // 291: determined.api.v1.GetTaskResponse + (*GetModelResponse)(nil), // 292: determined.api.v1.GetModelResponse + (*PostModelResponse)(nil), // 293: determined.api.v1.PostModelResponse + (*PatchModelResponse)(nil), // 294: determined.api.v1.PatchModelResponse + (*ArchiveModelResponse)(nil), // 295: determined.api.v1.ArchiveModelResponse + (*UnarchiveModelResponse)(nil), // 296: determined.api.v1.UnarchiveModelResponse + (*MoveModelResponse)(nil), // 297: determined.api.v1.MoveModelResponse + (*DeleteModelResponse)(nil), // 298: determined.api.v1.DeleteModelResponse + (*GetModelsResponse)(nil), // 299: determined.api.v1.GetModelsResponse + (*GetModelLabelsResponse)(nil), // 300: determined.api.v1.GetModelLabelsResponse + (*GetModelVersionResponse)(nil), // 301: determined.api.v1.GetModelVersionResponse + (*GetModelVersionsResponse)(nil), // 302: determined.api.v1.GetModelVersionsResponse + (*PostModelVersionResponse)(nil), // 303: determined.api.v1.PostModelVersionResponse + (*PatchModelVersionResponse)(nil), // 304: determined.api.v1.PatchModelVersionResponse + (*DeleteModelVersionResponse)(nil), // 305: determined.api.v1.DeleteModelVersionResponse + (*GetCheckpointResponse)(nil), // 306: determined.api.v1.GetCheckpointResponse + (*PostCheckpointMetadataResponse)(nil), // 307: determined.api.v1.PostCheckpointMetadataResponse + (*DeleteCheckpointsResponse)(nil), // 308: determined.api.v1.DeleteCheckpointsResponse + (*GetSearcherEventsResponse)(nil), // 309: determined.api.v1.GetSearcherEventsResponse + (*PostSearcherOperationsResponse)(nil), // 310: determined.api.v1.PostSearcherOperationsResponse + (*MetricNamesResponse)(nil), // 311: determined.api.v1.MetricNamesResponse + (*MetricBatchesResponse)(nil), // 312: determined.api.v1.MetricBatchesResponse + (*TrialsSnapshotResponse)(nil), // 313: determined.api.v1.TrialsSnapshotResponse + (*TrialsSampleResponse)(nil), // 314: determined.api.v1.TrialsSampleResponse + (*GetResourcePoolsResponse)(nil), // 315: determined.api.v1.GetResourcePoolsResponse + (*ComputeHPImportanceResponse)(nil), // 316: determined.api.v1.ComputeHPImportanceResponse + (*GetHPImportanceResponse)(nil), // 317: determined.api.v1.GetHPImportanceResponse + (*ResourceAllocationRawResponse)(nil), // 318: determined.api.v1.ResourceAllocationRawResponse + (*ResourceAllocationAggregatedResponse)(nil), // 319: determined.api.v1.ResourceAllocationAggregatedResponse + (*GetWorkspaceResponse)(nil), // 320: determined.api.v1.GetWorkspaceResponse + (*GetWorkspaceProjectsResponse)(nil), // 321: determined.api.v1.GetWorkspaceProjectsResponse + (*GetWorkspacesResponse)(nil), // 322: determined.api.v1.GetWorkspacesResponse + (*PostWorkspaceResponse)(nil), // 323: determined.api.v1.PostWorkspaceResponse + (*PatchWorkspaceResponse)(nil), // 324: determined.api.v1.PatchWorkspaceResponse + (*DeleteWorkspaceResponse)(nil), // 325: determined.api.v1.DeleteWorkspaceResponse + (*ArchiveWorkspaceResponse)(nil), // 326: determined.api.v1.ArchiveWorkspaceResponse + (*UnarchiveWorkspaceResponse)(nil), // 327: determined.api.v1.UnarchiveWorkspaceResponse + (*PinWorkspaceResponse)(nil), // 328: determined.api.v1.PinWorkspaceResponse + (*UnpinWorkspaceResponse)(nil), // 329: determined.api.v1.UnpinWorkspaceResponse + (*GetProjectResponse)(nil), // 330: determined.api.v1.GetProjectResponse + (*PostProjectResponse)(nil), // 331: determined.api.v1.PostProjectResponse + (*AddProjectNoteResponse)(nil), // 332: determined.api.v1.AddProjectNoteResponse + (*PutProjectNotesResponse)(nil), // 333: determined.api.v1.PutProjectNotesResponse + (*PatchProjectResponse)(nil), // 334: determined.api.v1.PatchProjectResponse + (*DeleteProjectResponse)(nil), // 335: determined.api.v1.DeleteProjectResponse + (*ArchiveProjectResponse)(nil), // 336: determined.api.v1.ArchiveProjectResponse + (*UnarchiveProjectResponse)(nil), // 337: determined.api.v1.UnarchiveProjectResponse + (*MoveProjectResponse)(nil), // 338: determined.api.v1.MoveProjectResponse + (*MoveExperimentResponse)(nil), // 339: determined.api.v1.MoveExperimentResponse + (*GetWebhooksResponse)(nil), // 340: determined.api.v1.GetWebhooksResponse + (*PostWebhookResponse)(nil), // 341: determined.api.v1.PostWebhookResponse + (*DeleteWebhookResponse)(nil), // 342: determined.api.v1.DeleteWebhookResponse + (*TestWebhookResponse)(nil), // 343: determined.api.v1.TestWebhookResponse + (*GetGroupResponse)(nil), // 344: determined.api.v1.GetGroupResponse + (*GetGroupsResponse)(nil), // 345: determined.api.v1.GetGroupsResponse + (*CreateGroupResponse)(nil), // 346: determined.api.v1.CreateGroupResponse + (*UpdateGroupResponse)(nil), // 347: determined.api.v1.UpdateGroupResponse + (*DeleteGroupResponse)(nil), // 348: determined.api.v1.DeleteGroupResponse + (*GetPermissionsSummaryResponse)(nil), // 349: determined.api.v1.GetPermissionsSummaryResponse + (*GetGroupsAndUsersAssignedToWorkspaceResponse)(nil), // 350: determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceResponse + (*GetRolesByIDResponse)(nil), // 351: determined.api.v1.GetRolesByIDResponse + (*GetRolesAssignedToUserResponse)(nil), // 352: determined.api.v1.GetRolesAssignedToUserResponse + (*GetRolesAssignedToGroupResponse)(nil), // 353: determined.api.v1.GetRolesAssignedToGroupResponse + (*SearchRolesAssignableToScopeResponse)(nil), // 354: determined.api.v1.SearchRolesAssignableToScopeResponse + (*ListRolesResponse)(nil), // 355: determined.api.v1.ListRolesResponse + (*AssignRolesResponse)(nil), // 356: determined.api.v1.AssignRolesResponse + (*RemoveAssignmentsResponse)(nil), // 357: determined.api.v1.RemoveAssignmentsResponse + (*PostUserActivityResponse)(nil), // 358: determined.api.v1.PostUserActivityResponse + (*GetProjectsByUserActivityResponse)(nil), // 359: determined.api.v1.GetProjectsByUserActivityResponse } var file_determined_api_v1_api_proto_depIdxs = []int32{ 0, // 0: determined.api.v1.Determined.Login:input_type -> determined.api.v1.LoginRequest @@ -2546,240 +2519,236 @@ var file_determined_api_v1_api_proto_depIdxs = []int32{ 129, // 129: determined.api.v1.Determined.GetSearcherEvents:input_type -> determined.api.v1.GetSearcherEventsRequest 130, // 130: determined.api.v1.Determined.PostSearcherOperations:input_type -> determined.api.v1.PostSearcherOperationsRequest 131, // 131: determined.api.v1.Determined.MetricNames:input_type -> determined.api.v1.MetricNamesRequest - 132, // 132: determined.api.v1.Determined.ExpCompareMetricNames:input_type -> determined.api.v1.ExpCompareMetricNamesRequest - 133, // 133: determined.api.v1.Determined.MetricBatches:input_type -> determined.api.v1.MetricBatchesRequest - 134, // 134: determined.api.v1.Determined.TrialsSnapshot:input_type -> determined.api.v1.TrialsSnapshotRequest - 135, // 135: determined.api.v1.Determined.TrialsSample:input_type -> determined.api.v1.TrialsSampleRequest - 136, // 136: determined.api.v1.Determined.ExpCompareTrialsSample:input_type -> determined.api.v1.ExpCompareTrialsSampleRequest - 137, // 137: determined.api.v1.Determined.GetResourcePools:input_type -> determined.api.v1.GetResourcePoolsRequest - 138, // 138: determined.api.v1.Determined.ComputeHPImportance:input_type -> determined.api.v1.ComputeHPImportanceRequest - 139, // 139: determined.api.v1.Determined.GetHPImportance:input_type -> determined.api.v1.GetHPImportanceRequest - 140, // 140: determined.api.v1.Determined.ResourceAllocationRaw:input_type -> determined.api.v1.ResourceAllocationRawRequest - 141, // 141: determined.api.v1.Determined.ResourceAllocationAggregated:input_type -> determined.api.v1.ResourceAllocationAggregatedRequest - 142, // 142: determined.api.v1.Determined.GetWorkspace:input_type -> determined.api.v1.GetWorkspaceRequest - 143, // 143: determined.api.v1.Determined.GetWorkspaceProjects:input_type -> determined.api.v1.GetWorkspaceProjectsRequest - 144, // 144: determined.api.v1.Determined.GetWorkspaces:input_type -> determined.api.v1.GetWorkspacesRequest - 145, // 145: determined.api.v1.Determined.PostWorkspace:input_type -> determined.api.v1.PostWorkspaceRequest - 146, // 146: determined.api.v1.Determined.PatchWorkspace:input_type -> determined.api.v1.PatchWorkspaceRequest - 147, // 147: determined.api.v1.Determined.DeleteWorkspace:input_type -> determined.api.v1.DeleteWorkspaceRequest - 148, // 148: determined.api.v1.Determined.ArchiveWorkspace:input_type -> determined.api.v1.ArchiveWorkspaceRequest - 149, // 149: determined.api.v1.Determined.UnarchiveWorkspace:input_type -> determined.api.v1.UnarchiveWorkspaceRequest - 150, // 150: determined.api.v1.Determined.PinWorkspace:input_type -> determined.api.v1.PinWorkspaceRequest - 151, // 151: determined.api.v1.Determined.UnpinWorkspace:input_type -> determined.api.v1.UnpinWorkspaceRequest - 152, // 152: determined.api.v1.Determined.GetProject:input_type -> determined.api.v1.GetProjectRequest - 153, // 153: determined.api.v1.Determined.PostProject:input_type -> determined.api.v1.PostProjectRequest - 154, // 154: determined.api.v1.Determined.AddProjectNote:input_type -> determined.api.v1.AddProjectNoteRequest - 155, // 155: determined.api.v1.Determined.PutProjectNotes:input_type -> determined.api.v1.PutProjectNotesRequest - 156, // 156: determined.api.v1.Determined.PatchProject:input_type -> determined.api.v1.PatchProjectRequest - 157, // 157: determined.api.v1.Determined.DeleteProject:input_type -> determined.api.v1.DeleteProjectRequest - 158, // 158: determined.api.v1.Determined.ArchiveProject:input_type -> determined.api.v1.ArchiveProjectRequest - 159, // 159: determined.api.v1.Determined.UnarchiveProject:input_type -> determined.api.v1.UnarchiveProjectRequest - 160, // 160: determined.api.v1.Determined.MoveProject:input_type -> determined.api.v1.MoveProjectRequest - 161, // 161: determined.api.v1.Determined.MoveExperiment:input_type -> determined.api.v1.MoveExperimentRequest - 162, // 162: determined.api.v1.Determined.GetWebhooks:input_type -> determined.api.v1.GetWebhooksRequest - 163, // 163: determined.api.v1.Determined.PostWebhook:input_type -> determined.api.v1.PostWebhookRequest - 164, // 164: determined.api.v1.Determined.DeleteWebhook:input_type -> determined.api.v1.DeleteWebhookRequest - 165, // 165: determined.api.v1.Determined.TestWebhook:input_type -> determined.api.v1.TestWebhookRequest - 166, // 166: determined.api.v1.Determined.GetGroup:input_type -> determined.api.v1.GetGroupRequest - 167, // 167: determined.api.v1.Determined.GetGroups:input_type -> determined.api.v1.GetGroupsRequest - 168, // 168: determined.api.v1.Determined.CreateGroup:input_type -> determined.api.v1.CreateGroupRequest - 169, // 169: determined.api.v1.Determined.UpdateGroup:input_type -> determined.api.v1.UpdateGroupRequest - 170, // 170: determined.api.v1.Determined.DeleteGroup:input_type -> determined.api.v1.DeleteGroupRequest - 171, // 171: determined.api.v1.Determined.GetPermissionsSummary:input_type -> determined.api.v1.GetPermissionsSummaryRequest - 172, // 172: determined.api.v1.Determined.GetGroupsAndUsersAssignedToWorkspace:input_type -> determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceRequest - 173, // 173: determined.api.v1.Determined.GetRolesByID:input_type -> determined.api.v1.GetRolesByIDRequest - 174, // 174: determined.api.v1.Determined.GetRolesAssignedToUser:input_type -> determined.api.v1.GetRolesAssignedToUserRequest - 175, // 175: determined.api.v1.Determined.GetRolesAssignedToGroup:input_type -> determined.api.v1.GetRolesAssignedToGroupRequest - 176, // 176: determined.api.v1.Determined.SearchRolesAssignableToScope:input_type -> determined.api.v1.SearchRolesAssignableToScopeRequest - 177, // 177: determined.api.v1.Determined.ListRoles:input_type -> determined.api.v1.ListRolesRequest - 178, // 178: determined.api.v1.Determined.AssignRoles:input_type -> determined.api.v1.AssignRolesRequest - 179, // 179: determined.api.v1.Determined.RemoveAssignments:input_type -> determined.api.v1.RemoveAssignmentsRequest - 180, // 180: determined.api.v1.Determined.PostUserActivity:input_type -> determined.api.v1.PostUserActivityRequest - 181, // 181: determined.api.v1.Determined.GetProjectsByUserActivity:input_type -> determined.api.v1.GetProjectsByUserActivityRequest - 182, // 182: determined.api.v1.Determined.Login:output_type -> determined.api.v1.LoginResponse - 183, // 183: determined.api.v1.Determined.CurrentUser:output_type -> determined.api.v1.CurrentUserResponse - 184, // 184: determined.api.v1.Determined.Logout:output_type -> determined.api.v1.LogoutResponse - 185, // 185: determined.api.v1.Determined.GetUsers:output_type -> determined.api.v1.GetUsersResponse - 186, // 186: determined.api.v1.Determined.GetUserSetting:output_type -> determined.api.v1.GetUserSettingResponse - 187, // 187: determined.api.v1.Determined.ResetUserSetting:output_type -> determined.api.v1.ResetUserSettingResponse - 188, // 188: determined.api.v1.Determined.PostUserSetting:output_type -> determined.api.v1.PostUserSettingResponse - 189, // 189: determined.api.v1.Determined.GetUser:output_type -> determined.api.v1.GetUserResponse - 190, // 190: determined.api.v1.Determined.GetUserByUsername:output_type -> determined.api.v1.GetUserByUsernameResponse - 191, // 191: determined.api.v1.Determined.GetMe:output_type -> determined.api.v1.GetMeResponse - 192, // 192: determined.api.v1.Determined.PostUser:output_type -> determined.api.v1.PostUserResponse - 193, // 193: determined.api.v1.Determined.SetUserPassword:output_type -> determined.api.v1.SetUserPasswordResponse - 194, // 194: determined.api.v1.Determined.PatchUser:output_type -> determined.api.v1.PatchUserResponse - 195, // 195: determined.api.v1.Determined.GetTelemetry:output_type -> determined.api.v1.GetTelemetryResponse - 196, // 196: determined.api.v1.Determined.GetMaster:output_type -> determined.api.v1.GetMasterResponse - 197, // 197: determined.api.v1.Determined.GetMasterConfig:output_type -> determined.api.v1.GetMasterConfigResponse - 198, // 198: determined.api.v1.Determined.MasterLogs:output_type -> determined.api.v1.MasterLogsResponse - 199, // 199: determined.api.v1.Determined.GetAgents:output_type -> determined.api.v1.GetAgentsResponse - 200, // 200: determined.api.v1.Determined.GetAgent:output_type -> determined.api.v1.GetAgentResponse - 201, // 201: determined.api.v1.Determined.GetSlots:output_type -> determined.api.v1.GetSlotsResponse - 202, // 202: determined.api.v1.Determined.GetSlot:output_type -> determined.api.v1.GetSlotResponse - 203, // 203: determined.api.v1.Determined.EnableAgent:output_type -> determined.api.v1.EnableAgentResponse - 204, // 204: determined.api.v1.Determined.DisableAgent:output_type -> determined.api.v1.DisableAgentResponse - 205, // 205: determined.api.v1.Determined.EnableSlot:output_type -> determined.api.v1.EnableSlotResponse - 206, // 206: determined.api.v1.Determined.DisableSlot:output_type -> determined.api.v1.DisableSlotResponse - 207, // 207: determined.api.v1.Determined.CreateExperiment:output_type -> determined.api.v1.CreateExperimentResponse - 208, // 208: determined.api.v1.Determined.GetExperiment:output_type -> determined.api.v1.GetExperimentResponse - 209, // 209: determined.api.v1.Determined.GetExperiments:output_type -> determined.api.v1.GetExperimentsResponse - 210, // 210: determined.api.v1.Determined.GetModelDef:output_type -> determined.api.v1.GetModelDefResponse - 211, // 211: determined.api.v1.Determined.GetModelDefTree:output_type -> determined.api.v1.GetModelDefTreeResponse - 212, // 212: determined.api.v1.Determined.GetModelDefFile:output_type -> determined.api.v1.GetModelDefFileResponse - 213, // 213: determined.api.v1.Determined.GetExperimentLabels:output_type -> determined.api.v1.GetExperimentLabelsResponse - 214, // 214: determined.api.v1.Determined.GetExperimentValidationHistory:output_type -> determined.api.v1.GetExperimentValidationHistoryResponse - 215, // 215: determined.api.v1.Determined.ActivateExperiment:output_type -> determined.api.v1.ActivateExperimentResponse - 216, // 216: determined.api.v1.Determined.PauseExperiment:output_type -> determined.api.v1.PauseExperimentResponse - 217, // 217: determined.api.v1.Determined.CancelExperiment:output_type -> determined.api.v1.CancelExperimentResponse - 218, // 218: determined.api.v1.Determined.KillExperiment:output_type -> determined.api.v1.KillExperimentResponse - 219, // 219: determined.api.v1.Determined.ArchiveExperiment:output_type -> determined.api.v1.ArchiveExperimentResponse - 220, // 220: determined.api.v1.Determined.UnarchiveExperiment:output_type -> determined.api.v1.UnarchiveExperimentResponse - 221, // 221: determined.api.v1.Determined.PatchExperiment:output_type -> determined.api.v1.PatchExperimentResponse - 222, // 222: determined.api.v1.Determined.DeleteExperiment:output_type -> determined.api.v1.DeleteExperimentResponse - 223, // 223: determined.api.v1.Determined.GetBestSearcherValidationMetric:output_type -> determined.api.v1.GetBestSearcherValidationMetricResponse - 224, // 224: determined.api.v1.Determined.GetExperimentCheckpoints:output_type -> determined.api.v1.GetExperimentCheckpointsResponse - 225, // 225: determined.api.v1.Determined.PreviewHPSearch:output_type -> determined.api.v1.PreviewHPSearchResponse - 226, // 226: determined.api.v1.Determined.GetExperimentTrials:output_type -> determined.api.v1.GetExperimentTrialsResponse - 227, // 227: determined.api.v1.Determined.CompareTrials:output_type -> determined.api.v1.CompareTrialsResponse - 228, // 228: determined.api.v1.Determined.QueryTrials:output_type -> determined.api.v1.QueryTrialsResponse - 229, // 229: determined.api.v1.Determined.UpdateTrialTags:output_type -> determined.api.v1.UpdateTrialTagsResponse - 230, // 230: determined.api.v1.Determined.GetTrialsCollections:output_type -> determined.api.v1.GetTrialsCollectionsResponse - 231, // 231: determined.api.v1.Determined.CreateTrialsCollection:output_type -> determined.api.v1.CreateTrialsCollectionResponse - 232, // 232: determined.api.v1.Determined.PatchTrialsCollection:output_type -> determined.api.v1.PatchTrialsCollectionResponse - 233, // 233: determined.api.v1.Determined.DeleteTrialsCollection:output_type -> determined.api.v1.DeleteTrialsCollectionResponse - 234, // 234: determined.api.v1.Determined.GetTrial:output_type -> determined.api.v1.GetTrialResponse - 235, // 235: determined.api.v1.Determined.GetTrialWorkloads:output_type -> determined.api.v1.GetTrialWorkloadsResponse - 236, // 236: determined.api.v1.Determined.TrialLogs:output_type -> determined.api.v1.TrialLogsResponse - 237, // 237: determined.api.v1.Determined.TrialLogsFields:output_type -> determined.api.v1.TrialLogsFieldsResponse - 238, // 238: determined.api.v1.Determined.SummarizeTrial:output_type -> determined.api.v1.SummarizeTrialResponse - 239, // 239: determined.api.v1.Determined.AllocationReady:output_type -> determined.api.v1.AllocationReadyResponse - 240, // 240: determined.api.v1.Determined.AllocationWaiting:output_type -> determined.api.v1.AllocationWaitingResponse - 241, // 241: determined.api.v1.Determined.TaskLogs:output_type -> determined.api.v1.TaskLogsResponse - 242, // 242: determined.api.v1.Determined.TaskLogsFields:output_type -> determined.api.v1.TaskLogsFieldsResponse - 243, // 243: determined.api.v1.Determined.GetTrialProfilerMetrics:output_type -> determined.api.v1.GetTrialProfilerMetricsResponse - 244, // 244: determined.api.v1.Determined.GetTrialProfilerAvailableSeries:output_type -> determined.api.v1.GetTrialProfilerAvailableSeriesResponse - 245, // 245: determined.api.v1.Determined.PostTrialProfilerMetricsBatch:output_type -> determined.api.v1.PostTrialProfilerMetricsBatchResponse - 246, // 246: determined.api.v1.Determined.KillTrial:output_type -> determined.api.v1.KillTrialResponse - 247, // 247: determined.api.v1.Determined.GetTrialCheckpoints:output_type -> determined.api.v1.GetTrialCheckpointsResponse - 248, // 248: determined.api.v1.Determined.AllocationPreemptionSignal:output_type -> determined.api.v1.AllocationPreemptionSignalResponse - 249, // 249: determined.api.v1.Determined.AllocationPendingPreemptionSignal:output_type -> determined.api.v1.AllocationPendingPreemptionSignalResponse - 250, // 250: determined.api.v1.Determined.AckAllocationPreemptionSignal:output_type -> determined.api.v1.AckAllocationPreemptionSignalResponse - 251, // 251: determined.api.v1.Determined.MarkAllocationResourcesDaemon:output_type -> determined.api.v1.MarkAllocationResourcesDaemonResponse - 252, // 252: determined.api.v1.Determined.AllocationRendezvousInfo:output_type -> determined.api.v1.AllocationRendezvousInfoResponse - 253, // 253: determined.api.v1.Determined.PostAllocationProxyAddress:output_type -> determined.api.v1.PostAllocationProxyAddressResponse - 254, // 254: determined.api.v1.Determined.AllocationAllGather:output_type -> determined.api.v1.AllocationAllGatherResponse - 255, // 255: determined.api.v1.Determined.NotifyContainerRunning:output_type -> determined.api.v1.NotifyContainerRunningResponse - 256, // 256: determined.api.v1.Determined.GetCurrentTrialSearcherOperation:output_type -> determined.api.v1.GetCurrentTrialSearcherOperationResponse - 257, // 257: determined.api.v1.Determined.CompleteTrialSearcherValidation:output_type -> determined.api.v1.CompleteTrialSearcherValidationResponse - 258, // 258: determined.api.v1.Determined.ReportTrialSearcherEarlyExit:output_type -> determined.api.v1.ReportTrialSearcherEarlyExitResponse - 259, // 259: determined.api.v1.Determined.ReportTrialProgress:output_type -> determined.api.v1.ReportTrialProgressResponse - 260, // 260: determined.api.v1.Determined.PostTrialRunnerMetadata:output_type -> determined.api.v1.PostTrialRunnerMetadataResponse - 261, // 261: determined.api.v1.Determined.ReportTrialTrainingMetrics:output_type -> determined.api.v1.ReportTrialTrainingMetricsResponse - 262, // 262: determined.api.v1.Determined.ReportTrialValidationMetrics:output_type -> determined.api.v1.ReportTrialValidationMetricsResponse - 263, // 263: determined.api.v1.Determined.ReportCheckpoint:output_type -> determined.api.v1.ReportCheckpointResponse - 264, // 264: determined.api.v1.Determined.GetJobs:output_type -> determined.api.v1.GetJobsResponse - 265, // 265: determined.api.v1.Determined.GetJobQueueStats:output_type -> determined.api.v1.GetJobQueueStatsResponse - 266, // 266: determined.api.v1.Determined.UpdateJobQueue:output_type -> determined.api.v1.UpdateJobQueueResponse - 267, // 267: determined.api.v1.Determined.GetTemplates:output_type -> determined.api.v1.GetTemplatesResponse - 268, // 268: determined.api.v1.Determined.GetTemplate:output_type -> determined.api.v1.GetTemplateResponse - 269, // 269: determined.api.v1.Determined.PutTemplate:output_type -> determined.api.v1.PutTemplateResponse - 270, // 270: determined.api.v1.Determined.DeleteTemplate:output_type -> determined.api.v1.DeleteTemplateResponse - 271, // 271: determined.api.v1.Determined.GetNotebooks:output_type -> determined.api.v1.GetNotebooksResponse - 272, // 272: determined.api.v1.Determined.GetNotebook:output_type -> determined.api.v1.GetNotebookResponse - 273, // 273: determined.api.v1.Determined.IdleNotebook:output_type -> determined.api.v1.IdleNotebookResponse - 274, // 274: determined.api.v1.Determined.KillNotebook:output_type -> determined.api.v1.KillNotebookResponse - 275, // 275: determined.api.v1.Determined.SetNotebookPriority:output_type -> determined.api.v1.SetNotebookPriorityResponse - 276, // 276: determined.api.v1.Determined.LaunchNotebook:output_type -> determined.api.v1.LaunchNotebookResponse - 277, // 277: determined.api.v1.Determined.GetShells:output_type -> determined.api.v1.GetShellsResponse - 278, // 278: determined.api.v1.Determined.GetShell:output_type -> determined.api.v1.GetShellResponse - 279, // 279: determined.api.v1.Determined.KillShell:output_type -> determined.api.v1.KillShellResponse - 280, // 280: determined.api.v1.Determined.SetShellPriority:output_type -> determined.api.v1.SetShellPriorityResponse - 281, // 281: determined.api.v1.Determined.LaunchShell:output_type -> determined.api.v1.LaunchShellResponse - 282, // 282: determined.api.v1.Determined.GetCommands:output_type -> determined.api.v1.GetCommandsResponse - 283, // 283: determined.api.v1.Determined.GetCommand:output_type -> determined.api.v1.GetCommandResponse - 284, // 284: determined.api.v1.Determined.KillCommand:output_type -> determined.api.v1.KillCommandResponse - 285, // 285: determined.api.v1.Determined.SetCommandPriority:output_type -> determined.api.v1.SetCommandPriorityResponse - 286, // 286: determined.api.v1.Determined.LaunchCommand:output_type -> determined.api.v1.LaunchCommandResponse - 287, // 287: determined.api.v1.Determined.GetTensorboards:output_type -> determined.api.v1.GetTensorboardsResponse - 288, // 288: determined.api.v1.Determined.GetTensorboard:output_type -> determined.api.v1.GetTensorboardResponse - 289, // 289: determined.api.v1.Determined.KillTensorboard:output_type -> determined.api.v1.KillTensorboardResponse - 290, // 290: determined.api.v1.Determined.SetTensorboardPriority:output_type -> determined.api.v1.SetTensorboardPriorityResponse - 291, // 291: determined.api.v1.Determined.LaunchTensorboard:output_type -> determined.api.v1.LaunchTensorboardResponse - 292, // 292: determined.api.v1.Determined.GetActiveTasksCount:output_type -> determined.api.v1.GetActiveTasksCountResponse - 293, // 293: determined.api.v1.Determined.GetTask:output_type -> determined.api.v1.GetTaskResponse - 294, // 294: determined.api.v1.Determined.GetModel:output_type -> determined.api.v1.GetModelResponse - 295, // 295: determined.api.v1.Determined.PostModel:output_type -> determined.api.v1.PostModelResponse - 296, // 296: determined.api.v1.Determined.PatchModel:output_type -> determined.api.v1.PatchModelResponse - 297, // 297: determined.api.v1.Determined.ArchiveModel:output_type -> determined.api.v1.ArchiveModelResponse - 298, // 298: determined.api.v1.Determined.UnarchiveModel:output_type -> determined.api.v1.UnarchiveModelResponse - 299, // 299: determined.api.v1.Determined.MoveModel:output_type -> determined.api.v1.MoveModelResponse - 300, // 300: determined.api.v1.Determined.DeleteModel:output_type -> determined.api.v1.DeleteModelResponse - 301, // 301: determined.api.v1.Determined.GetModels:output_type -> determined.api.v1.GetModelsResponse - 302, // 302: determined.api.v1.Determined.GetModelLabels:output_type -> determined.api.v1.GetModelLabelsResponse - 303, // 303: determined.api.v1.Determined.GetModelVersion:output_type -> determined.api.v1.GetModelVersionResponse - 304, // 304: determined.api.v1.Determined.GetModelVersions:output_type -> determined.api.v1.GetModelVersionsResponse - 305, // 305: determined.api.v1.Determined.PostModelVersion:output_type -> determined.api.v1.PostModelVersionResponse - 306, // 306: determined.api.v1.Determined.PatchModelVersion:output_type -> determined.api.v1.PatchModelVersionResponse - 307, // 307: determined.api.v1.Determined.DeleteModelVersion:output_type -> determined.api.v1.DeleteModelVersionResponse - 308, // 308: determined.api.v1.Determined.GetCheckpoint:output_type -> determined.api.v1.GetCheckpointResponse - 309, // 309: determined.api.v1.Determined.PostCheckpointMetadata:output_type -> determined.api.v1.PostCheckpointMetadataResponse - 310, // 310: determined.api.v1.Determined.DeleteCheckpoints:output_type -> determined.api.v1.DeleteCheckpointsResponse - 311, // 311: determined.api.v1.Determined.GetSearcherEvents:output_type -> determined.api.v1.GetSearcherEventsResponse - 312, // 312: determined.api.v1.Determined.PostSearcherOperations:output_type -> determined.api.v1.PostSearcherOperationsResponse - 313, // 313: determined.api.v1.Determined.MetricNames:output_type -> determined.api.v1.MetricNamesResponse - 314, // 314: determined.api.v1.Determined.ExpCompareMetricNames:output_type -> determined.api.v1.ExpCompareMetricNamesResponse - 315, // 315: determined.api.v1.Determined.MetricBatches:output_type -> determined.api.v1.MetricBatchesResponse - 316, // 316: determined.api.v1.Determined.TrialsSnapshot:output_type -> determined.api.v1.TrialsSnapshotResponse - 317, // 317: determined.api.v1.Determined.TrialsSample:output_type -> determined.api.v1.TrialsSampleResponse - 318, // 318: determined.api.v1.Determined.ExpCompareTrialsSample:output_type -> determined.api.v1.ExpCompareTrialsSampleResponse - 319, // 319: determined.api.v1.Determined.GetResourcePools:output_type -> determined.api.v1.GetResourcePoolsResponse - 320, // 320: determined.api.v1.Determined.ComputeHPImportance:output_type -> determined.api.v1.ComputeHPImportanceResponse - 321, // 321: determined.api.v1.Determined.GetHPImportance:output_type -> determined.api.v1.GetHPImportanceResponse - 322, // 322: determined.api.v1.Determined.ResourceAllocationRaw:output_type -> determined.api.v1.ResourceAllocationRawResponse - 323, // 323: determined.api.v1.Determined.ResourceAllocationAggregated:output_type -> determined.api.v1.ResourceAllocationAggregatedResponse - 324, // 324: determined.api.v1.Determined.GetWorkspace:output_type -> determined.api.v1.GetWorkspaceResponse - 325, // 325: determined.api.v1.Determined.GetWorkspaceProjects:output_type -> determined.api.v1.GetWorkspaceProjectsResponse - 326, // 326: determined.api.v1.Determined.GetWorkspaces:output_type -> determined.api.v1.GetWorkspacesResponse - 327, // 327: determined.api.v1.Determined.PostWorkspace:output_type -> determined.api.v1.PostWorkspaceResponse - 328, // 328: determined.api.v1.Determined.PatchWorkspace:output_type -> determined.api.v1.PatchWorkspaceResponse - 329, // 329: determined.api.v1.Determined.DeleteWorkspace:output_type -> determined.api.v1.DeleteWorkspaceResponse - 330, // 330: determined.api.v1.Determined.ArchiveWorkspace:output_type -> determined.api.v1.ArchiveWorkspaceResponse - 331, // 331: determined.api.v1.Determined.UnarchiveWorkspace:output_type -> determined.api.v1.UnarchiveWorkspaceResponse - 332, // 332: determined.api.v1.Determined.PinWorkspace:output_type -> determined.api.v1.PinWorkspaceResponse - 333, // 333: determined.api.v1.Determined.UnpinWorkspace:output_type -> determined.api.v1.UnpinWorkspaceResponse - 334, // 334: determined.api.v1.Determined.GetProject:output_type -> determined.api.v1.GetProjectResponse - 335, // 335: determined.api.v1.Determined.PostProject:output_type -> determined.api.v1.PostProjectResponse - 336, // 336: determined.api.v1.Determined.AddProjectNote:output_type -> determined.api.v1.AddProjectNoteResponse - 337, // 337: determined.api.v1.Determined.PutProjectNotes:output_type -> determined.api.v1.PutProjectNotesResponse - 338, // 338: determined.api.v1.Determined.PatchProject:output_type -> determined.api.v1.PatchProjectResponse - 339, // 339: determined.api.v1.Determined.DeleteProject:output_type -> determined.api.v1.DeleteProjectResponse - 340, // 340: determined.api.v1.Determined.ArchiveProject:output_type -> determined.api.v1.ArchiveProjectResponse - 341, // 341: determined.api.v1.Determined.UnarchiveProject:output_type -> determined.api.v1.UnarchiveProjectResponse - 342, // 342: determined.api.v1.Determined.MoveProject:output_type -> determined.api.v1.MoveProjectResponse - 343, // 343: determined.api.v1.Determined.MoveExperiment:output_type -> determined.api.v1.MoveExperimentResponse - 344, // 344: determined.api.v1.Determined.GetWebhooks:output_type -> determined.api.v1.GetWebhooksResponse - 345, // 345: determined.api.v1.Determined.PostWebhook:output_type -> determined.api.v1.PostWebhookResponse - 346, // 346: determined.api.v1.Determined.DeleteWebhook:output_type -> determined.api.v1.DeleteWebhookResponse - 347, // 347: determined.api.v1.Determined.TestWebhook:output_type -> determined.api.v1.TestWebhookResponse - 348, // 348: determined.api.v1.Determined.GetGroup:output_type -> determined.api.v1.GetGroupResponse - 349, // 349: determined.api.v1.Determined.GetGroups:output_type -> determined.api.v1.GetGroupsResponse - 350, // 350: determined.api.v1.Determined.CreateGroup:output_type -> determined.api.v1.CreateGroupResponse - 351, // 351: determined.api.v1.Determined.UpdateGroup:output_type -> determined.api.v1.UpdateGroupResponse - 352, // 352: determined.api.v1.Determined.DeleteGroup:output_type -> determined.api.v1.DeleteGroupResponse - 353, // 353: determined.api.v1.Determined.GetPermissionsSummary:output_type -> determined.api.v1.GetPermissionsSummaryResponse - 354, // 354: determined.api.v1.Determined.GetGroupsAndUsersAssignedToWorkspace:output_type -> determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceResponse - 355, // 355: determined.api.v1.Determined.GetRolesByID:output_type -> determined.api.v1.GetRolesByIDResponse - 356, // 356: determined.api.v1.Determined.GetRolesAssignedToUser:output_type -> determined.api.v1.GetRolesAssignedToUserResponse - 357, // 357: determined.api.v1.Determined.GetRolesAssignedToGroup:output_type -> determined.api.v1.GetRolesAssignedToGroupResponse - 358, // 358: determined.api.v1.Determined.SearchRolesAssignableToScope:output_type -> determined.api.v1.SearchRolesAssignableToScopeResponse - 359, // 359: determined.api.v1.Determined.ListRoles:output_type -> determined.api.v1.ListRolesResponse - 360, // 360: determined.api.v1.Determined.AssignRoles:output_type -> determined.api.v1.AssignRolesResponse - 361, // 361: determined.api.v1.Determined.RemoveAssignments:output_type -> determined.api.v1.RemoveAssignmentsResponse - 362, // 362: determined.api.v1.Determined.PostUserActivity:output_type -> determined.api.v1.PostUserActivityResponse - 363, // 363: determined.api.v1.Determined.GetProjectsByUserActivity:output_type -> determined.api.v1.GetProjectsByUserActivityResponse - 182, // [182:364] is the sub-list for method output_type - 0, // [0:182] is the sub-list for method input_type + 132, // 132: determined.api.v1.Determined.MetricBatches:input_type -> determined.api.v1.MetricBatchesRequest + 133, // 133: determined.api.v1.Determined.TrialsSnapshot:input_type -> determined.api.v1.TrialsSnapshotRequest + 134, // 134: determined.api.v1.Determined.TrialsSample:input_type -> determined.api.v1.TrialsSampleRequest + 135, // 135: determined.api.v1.Determined.GetResourcePools:input_type -> determined.api.v1.GetResourcePoolsRequest + 136, // 136: determined.api.v1.Determined.ComputeHPImportance:input_type -> determined.api.v1.ComputeHPImportanceRequest + 137, // 137: determined.api.v1.Determined.GetHPImportance:input_type -> determined.api.v1.GetHPImportanceRequest + 138, // 138: determined.api.v1.Determined.ResourceAllocationRaw:input_type -> determined.api.v1.ResourceAllocationRawRequest + 139, // 139: determined.api.v1.Determined.ResourceAllocationAggregated:input_type -> determined.api.v1.ResourceAllocationAggregatedRequest + 140, // 140: determined.api.v1.Determined.GetWorkspace:input_type -> determined.api.v1.GetWorkspaceRequest + 141, // 141: determined.api.v1.Determined.GetWorkspaceProjects:input_type -> determined.api.v1.GetWorkspaceProjectsRequest + 142, // 142: determined.api.v1.Determined.GetWorkspaces:input_type -> determined.api.v1.GetWorkspacesRequest + 143, // 143: determined.api.v1.Determined.PostWorkspace:input_type -> determined.api.v1.PostWorkspaceRequest + 144, // 144: determined.api.v1.Determined.PatchWorkspace:input_type -> determined.api.v1.PatchWorkspaceRequest + 145, // 145: determined.api.v1.Determined.DeleteWorkspace:input_type -> determined.api.v1.DeleteWorkspaceRequest + 146, // 146: determined.api.v1.Determined.ArchiveWorkspace:input_type -> determined.api.v1.ArchiveWorkspaceRequest + 147, // 147: determined.api.v1.Determined.UnarchiveWorkspace:input_type -> determined.api.v1.UnarchiveWorkspaceRequest + 148, // 148: determined.api.v1.Determined.PinWorkspace:input_type -> determined.api.v1.PinWorkspaceRequest + 149, // 149: determined.api.v1.Determined.UnpinWorkspace:input_type -> determined.api.v1.UnpinWorkspaceRequest + 150, // 150: determined.api.v1.Determined.GetProject:input_type -> determined.api.v1.GetProjectRequest + 151, // 151: determined.api.v1.Determined.PostProject:input_type -> determined.api.v1.PostProjectRequest + 152, // 152: determined.api.v1.Determined.AddProjectNote:input_type -> determined.api.v1.AddProjectNoteRequest + 153, // 153: determined.api.v1.Determined.PutProjectNotes:input_type -> determined.api.v1.PutProjectNotesRequest + 154, // 154: determined.api.v1.Determined.PatchProject:input_type -> determined.api.v1.PatchProjectRequest + 155, // 155: determined.api.v1.Determined.DeleteProject:input_type -> determined.api.v1.DeleteProjectRequest + 156, // 156: determined.api.v1.Determined.ArchiveProject:input_type -> determined.api.v1.ArchiveProjectRequest + 157, // 157: determined.api.v1.Determined.UnarchiveProject:input_type -> determined.api.v1.UnarchiveProjectRequest + 158, // 158: determined.api.v1.Determined.MoveProject:input_type -> determined.api.v1.MoveProjectRequest + 159, // 159: determined.api.v1.Determined.MoveExperiment:input_type -> determined.api.v1.MoveExperimentRequest + 160, // 160: determined.api.v1.Determined.GetWebhooks:input_type -> determined.api.v1.GetWebhooksRequest + 161, // 161: determined.api.v1.Determined.PostWebhook:input_type -> determined.api.v1.PostWebhookRequest + 162, // 162: determined.api.v1.Determined.DeleteWebhook:input_type -> determined.api.v1.DeleteWebhookRequest + 163, // 163: determined.api.v1.Determined.TestWebhook:input_type -> determined.api.v1.TestWebhookRequest + 164, // 164: determined.api.v1.Determined.GetGroup:input_type -> determined.api.v1.GetGroupRequest + 165, // 165: determined.api.v1.Determined.GetGroups:input_type -> determined.api.v1.GetGroupsRequest + 166, // 166: determined.api.v1.Determined.CreateGroup:input_type -> determined.api.v1.CreateGroupRequest + 167, // 167: determined.api.v1.Determined.UpdateGroup:input_type -> determined.api.v1.UpdateGroupRequest + 168, // 168: determined.api.v1.Determined.DeleteGroup:input_type -> determined.api.v1.DeleteGroupRequest + 169, // 169: determined.api.v1.Determined.GetPermissionsSummary:input_type -> determined.api.v1.GetPermissionsSummaryRequest + 170, // 170: determined.api.v1.Determined.GetGroupsAndUsersAssignedToWorkspace:input_type -> determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceRequest + 171, // 171: determined.api.v1.Determined.GetRolesByID:input_type -> determined.api.v1.GetRolesByIDRequest + 172, // 172: determined.api.v1.Determined.GetRolesAssignedToUser:input_type -> determined.api.v1.GetRolesAssignedToUserRequest + 173, // 173: determined.api.v1.Determined.GetRolesAssignedToGroup:input_type -> determined.api.v1.GetRolesAssignedToGroupRequest + 174, // 174: determined.api.v1.Determined.SearchRolesAssignableToScope:input_type -> determined.api.v1.SearchRolesAssignableToScopeRequest + 175, // 175: determined.api.v1.Determined.ListRoles:input_type -> determined.api.v1.ListRolesRequest + 176, // 176: determined.api.v1.Determined.AssignRoles:input_type -> determined.api.v1.AssignRolesRequest + 177, // 177: determined.api.v1.Determined.RemoveAssignments:input_type -> determined.api.v1.RemoveAssignmentsRequest + 178, // 178: determined.api.v1.Determined.PostUserActivity:input_type -> determined.api.v1.PostUserActivityRequest + 179, // 179: determined.api.v1.Determined.GetProjectsByUserActivity:input_type -> determined.api.v1.GetProjectsByUserActivityRequest + 180, // 180: determined.api.v1.Determined.Login:output_type -> determined.api.v1.LoginResponse + 181, // 181: determined.api.v1.Determined.CurrentUser:output_type -> determined.api.v1.CurrentUserResponse + 182, // 182: determined.api.v1.Determined.Logout:output_type -> determined.api.v1.LogoutResponse + 183, // 183: determined.api.v1.Determined.GetUsers:output_type -> determined.api.v1.GetUsersResponse + 184, // 184: determined.api.v1.Determined.GetUserSetting:output_type -> determined.api.v1.GetUserSettingResponse + 185, // 185: determined.api.v1.Determined.ResetUserSetting:output_type -> determined.api.v1.ResetUserSettingResponse + 186, // 186: determined.api.v1.Determined.PostUserSetting:output_type -> determined.api.v1.PostUserSettingResponse + 187, // 187: determined.api.v1.Determined.GetUser:output_type -> determined.api.v1.GetUserResponse + 188, // 188: determined.api.v1.Determined.GetUserByUsername:output_type -> determined.api.v1.GetUserByUsernameResponse + 189, // 189: determined.api.v1.Determined.GetMe:output_type -> determined.api.v1.GetMeResponse + 190, // 190: determined.api.v1.Determined.PostUser:output_type -> determined.api.v1.PostUserResponse + 191, // 191: determined.api.v1.Determined.SetUserPassword:output_type -> determined.api.v1.SetUserPasswordResponse + 192, // 192: determined.api.v1.Determined.PatchUser:output_type -> determined.api.v1.PatchUserResponse + 193, // 193: determined.api.v1.Determined.GetTelemetry:output_type -> determined.api.v1.GetTelemetryResponse + 194, // 194: determined.api.v1.Determined.GetMaster:output_type -> determined.api.v1.GetMasterResponse + 195, // 195: determined.api.v1.Determined.GetMasterConfig:output_type -> determined.api.v1.GetMasterConfigResponse + 196, // 196: determined.api.v1.Determined.MasterLogs:output_type -> determined.api.v1.MasterLogsResponse + 197, // 197: determined.api.v1.Determined.GetAgents:output_type -> determined.api.v1.GetAgentsResponse + 198, // 198: determined.api.v1.Determined.GetAgent:output_type -> determined.api.v1.GetAgentResponse + 199, // 199: determined.api.v1.Determined.GetSlots:output_type -> determined.api.v1.GetSlotsResponse + 200, // 200: determined.api.v1.Determined.GetSlot:output_type -> determined.api.v1.GetSlotResponse + 201, // 201: determined.api.v1.Determined.EnableAgent:output_type -> determined.api.v1.EnableAgentResponse + 202, // 202: determined.api.v1.Determined.DisableAgent:output_type -> determined.api.v1.DisableAgentResponse + 203, // 203: determined.api.v1.Determined.EnableSlot:output_type -> determined.api.v1.EnableSlotResponse + 204, // 204: determined.api.v1.Determined.DisableSlot:output_type -> determined.api.v1.DisableSlotResponse + 205, // 205: determined.api.v1.Determined.CreateExperiment:output_type -> determined.api.v1.CreateExperimentResponse + 206, // 206: determined.api.v1.Determined.GetExperiment:output_type -> determined.api.v1.GetExperimentResponse + 207, // 207: determined.api.v1.Determined.GetExperiments:output_type -> determined.api.v1.GetExperimentsResponse + 208, // 208: determined.api.v1.Determined.GetModelDef:output_type -> determined.api.v1.GetModelDefResponse + 209, // 209: determined.api.v1.Determined.GetModelDefTree:output_type -> determined.api.v1.GetModelDefTreeResponse + 210, // 210: determined.api.v1.Determined.GetModelDefFile:output_type -> determined.api.v1.GetModelDefFileResponse + 211, // 211: determined.api.v1.Determined.GetExperimentLabels:output_type -> determined.api.v1.GetExperimentLabelsResponse + 212, // 212: determined.api.v1.Determined.GetExperimentValidationHistory:output_type -> determined.api.v1.GetExperimentValidationHistoryResponse + 213, // 213: determined.api.v1.Determined.ActivateExperiment:output_type -> determined.api.v1.ActivateExperimentResponse + 214, // 214: determined.api.v1.Determined.PauseExperiment:output_type -> determined.api.v1.PauseExperimentResponse + 215, // 215: determined.api.v1.Determined.CancelExperiment:output_type -> determined.api.v1.CancelExperimentResponse + 216, // 216: determined.api.v1.Determined.KillExperiment:output_type -> determined.api.v1.KillExperimentResponse + 217, // 217: determined.api.v1.Determined.ArchiveExperiment:output_type -> determined.api.v1.ArchiveExperimentResponse + 218, // 218: determined.api.v1.Determined.UnarchiveExperiment:output_type -> determined.api.v1.UnarchiveExperimentResponse + 219, // 219: determined.api.v1.Determined.PatchExperiment:output_type -> determined.api.v1.PatchExperimentResponse + 220, // 220: determined.api.v1.Determined.DeleteExperiment:output_type -> determined.api.v1.DeleteExperimentResponse + 221, // 221: determined.api.v1.Determined.GetBestSearcherValidationMetric:output_type -> determined.api.v1.GetBestSearcherValidationMetricResponse + 222, // 222: determined.api.v1.Determined.GetExperimentCheckpoints:output_type -> determined.api.v1.GetExperimentCheckpointsResponse + 223, // 223: determined.api.v1.Determined.PreviewHPSearch:output_type -> determined.api.v1.PreviewHPSearchResponse + 224, // 224: determined.api.v1.Determined.GetExperimentTrials:output_type -> determined.api.v1.GetExperimentTrialsResponse + 225, // 225: determined.api.v1.Determined.CompareTrials:output_type -> determined.api.v1.CompareTrialsResponse + 226, // 226: determined.api.v1.Determined.QueryTrials:output_type -> determined.api.v1.QueryTrialsResponse + 227, // 227: determined.api.v1.Determined.UpdateTrialTags:output_type -> determined.api.v1.UpdateTrialTagsResponse + 228, // 228: determined.api.v1.Determined.GetTrialsCollections:output_type -> determined.api.v1.GetTrialsCollectionsResponse + 229, // 229: determined.api.v1.Determined.CreateTrialsCollection:output_type -> determined.api.v1.CreateTrialsCollectionResponse + 230, // 230: determined.api.v1.Determined.PatchTrialsCollection:output_type -> determined.api.v1.PatchTrialsCollectionResponse + 231, // 231: determined.api.v1.Determined.DeleteTrialsCollection:output_type -> determined.api.v1.DeleteTrialsCollectionResponse + 232, // 232: determined.api.v1.Determined.GetTrial:output_type -> determined.api.v1.GetTrialResponse + 233, // 233: determined.api.v1.Determined.GetTrialWorkloads:output_type -> determined.api.v1.GetTrialWorkloadsResponse + 234, // 234: determined.api.v1.Determined.TrialLogs:output_type -> determined.api.v1.TrialLogsResponse + 235, // 235: determined.api.v1.Determined.TrialLogsFields:output_type -> determined.api.v1.TrialLogsFieldsResponse + 236, // 236: determined.api.v1.Determined.SummarizeTrial:output_type -> determined.api.v1.SummarizeTrialResponse + 237, // 237: determined.api.v1.Determined.AllocationReady:output_type -> determined.api.v1.AllocationReadyResponse + 238, // 238: determined.api.v1.Determined.AllocationWaiting:output_type -> determined.api.v1.AllocationWaitingResponse + 239, // 239: determined.api.v1.Determined.TaskLogs:output_type -> determined.api.v1.TaskLogsResponse + 240, // 240: determined.api.v1.Determined.TaskLogsFields:output_type -> determined.api.v1.TaskLogsFieldsResponse + 241, // 241: determined.api.v1.Determined.GetTrialProfilerMetrics:output_type -> determined.api.v1.GetTrialProfilerMetricsResponse + 242, // 242: determined.api.v1.Determined.GetTrialProfilerAvailableSeries:output_type -> determined.api.v1.GetTrialProfilerAvailableSeriesResponse + 243, // 243: determined.api.v1.Determined.PostTrialProfilerMetricsBatch:output_type -> determined.api.v1.PostTrialProfilerMetricsBatchResponse + 244, // 244: determined.api.v1.Determined.KillTrial:output_type -> determined.api.v1.KillTrialResponse + 245, // 245: determined.api.v1.Determined.GetTrialCheckpoints:output_type -> determined.api.v1.GetTrialCheckpointsResponse + 246, // 246: determined.api.v1.Determined.AllocationPreemptionSignal:output_type -> determined.api.v1.AllocationPreemptionSignalResponse + 247, // 247: determined.api.v1.Determined.AllocationPendingPreemptionSignal:output_type -> determined.api.v1.AllocationPendingPreemptionSignalResponse + 248, // 248: determined.api.v1.Determined.AckAllocationPreemptionSignal:output_type -> determined.api.v1.AckAllocationPreemptionSignalResponse + 249, // 249: determined.api.v1.Determined.MarkAllocationResourcesDaemon:output_type -> determined.api.v1.MarkAllocationResourcesDaemonResponse + 250, // 250: determined.api.v1.Determined.AllocationRendezvousInfo:output_type -> determined.api.v1.AllocationRendezvousInfoResponse + 251, // 251: determined.api.v1.Determined.PostAllocationProxyAddress:output_type -> determined.api.v1.PostAllocationProxyAddressResponse + 252, // 252: determined.api.v1.Determined.AllocationAllGather:output_type -> determined.api.v1.AllocationAllGatherResponse + 253, // 253: determined.api.v1.Determined.NotifyContainerRunning:output_type -> determined.api.v1.NotifyContainerRunningResponse + 254, // 254: determined.api.v1.Determined.GetCurrentTrialSearcherOperation:output_type -> determined.api.v1.GetCurrentTrialSearcherOperationResponse + 255, // 255: determined.api.v1.Determined.CompleteTrialSearcherValidation:output_type -> determined.api.v1.CompleteTrialSearcherValidationResponse + 256, // 256: determined.api.v1.Determined.ReportTrialSearcherEarlyExit:output_type -> determined.api.v1.ReportTrialSearcherEarlyExitResponse + 257, // 257: determined.api.v1.Determined.ReportTrialProgress:output_type -> determined.api.v1.ReportTrialProgressResponse + 258, // 258: determined.api.v1.Determined.PostTrialRunnerMetadata:output_type -> determined.api.v1.PostTrialRunnerMetadataResponse + 259, // 259: determined.api.v1.Determined.ReportTrialTrainingMetrics:output_type -> determined.api.v1.ReportTrialTrainingMetricsResponse + 260, // 260: determined.api.v1.Determined.ReportTrialValidationMetrics:output_type -> determined.api.v1.ReportTrialValidationMetricsResponse + 261, // 261: determined.api.v1.Determined.ReportCheckpoint:output_type -> determined.api.v1.ReportCheckpointResponse + 262, // 262: determined.api.v1.Determined.GetJobs:output_type -> determined.api.v1.GetJobsResponse + 263, // 263: determined.api.v1.Determined.GetJobQueueStats:output_type -> determined.api.v1.GetJobQueueStatsResponse + 264, // 264: determined.api.v1.Determined.UpdateJobQueue:output_type -> determined.api.v1.UpdateJobQueueResponse + 265, // 265: determined.api.v1.Determined.GetTemplates:output_type -> determined.api.v1.GetTemplatesResponse + 266, // 266: determined.api.v1.Determined.GetTemplate:output_type -> determined.api.v1.GetTemplateResponse + 267, // 267: determined.api.v1.Determined.PutTemplate:output_type -> determined.api.v1.PutTemplateResponse + 268, // 268: determined.api.v1.Determined.DeleteTemplate:output_type -> determined.api.v1.DeleteTemplateResponse + 269, // 269: determined.api.v1.Determined.GetNotebooks:output_type -> determined.api.v1.GetNotebooksResponse + 270, // 270: determined.api.v1.Determined.GetNotebook:output_type -> determined.api.v1.GetNotebookResponse + 271, // 271: determined.api.v1.Determined.IdleNotebook:output_type -> determined.api.v1.IdleNotebookResponse + 272, // 272: determined.api.v1.Determined.KillNotebook:output_type -> determined.api.v1.KillNotebookResponse + 273, // 273: determined.api.v1.Determined.SetNotebookPriority:output_type -> determined.api.v1.SetNotebookPriorityResponse + 274, // 274: determined.api.v1.Determined.LaunchNotebook:output_type -> determined.api.v1.LaunchNotebookResponse + 275, // 275: determined.api.v1.Determined.GetShells:output_type -> determined.api.v1.GetShellsResponse + 276, // 276: determined.api.v1.Determined.GetShell:output_type -> determined.api.v1.GetShellResponse + 277, // 277: determined.api.v1.Determined.KillShell:output_type -> determined.api.v1.KillShellResponse + 278, // 278: determined.api.v1.Determined.SetShellPriority:output_type -> determined.api.v1.SetShellPriorityResponse + 279, // 279: determined.api.v1.Determined.LaunchShell:output_type -> determined.api.v1.LaunchShellResponse + 280, // 280: determined.api.v1.Determined.GetCommands:output_type -> determined.api.v1.GetCommandsResponse + 281, // 281: determined.api.v1.Determined.GetCommand:output_type -> determined.api.v1.GetCommandResponse + 282, // 282: determined.api.v1.Determined.KillCommand:output_type -> determined.api.v1.KillCommandResponse + 283, // 283: determined.api.v1.Determined.SetCommandPriority:output_type -> determined.api.v1.SetCommandPriorityResponse + 284, // 284: determined.api.v1.Determined.LaunchCommand:output_type -> determined.api.v1.LaunchCommandResponse + 285, // 285: determined.api.v1.Determined.GetTensorboards:output_type -> determined.api.v1.GetTensorboardsResponse + 286, // 286: determined.api.v1.Determined.GetTensorboard:output_type -> determined.api.v1.GetTensorboardResponse + 287, // 287: determined.api.v1.Determined.KillTensorboard:output_type -> determined.api.v1.KillTensorboardResponse + 288, // 288: determined.api.v1.Determined.SetTensorboardPriority:output_type -> determined.api.v1.SetTensorboardPriorityResponse + 289, // 289: determined.api.v1.Determined.LaunchTensorboard:output_type -> determined.api.v1.LaunchTensorboardResponse + 290, // 290: determined.api.v1.Determined.GetActiveTasksCount:output_type -> determined.api.v1.GetActiveTasksCountResponse + 291, // 291: determined.api.v1.Determined.GetTask:output_type -> determined.api.v1.GetTaskResponse + 292, // 292: determined.api.v1.Determined.GetModel:output_type -> determined.api.v1.GetModelResponse + 293, // 293: determined.api.v1.Determined.PostModel:output_type -> determined.api.v1.PostModelResponse + 294, // 294: determined.api.v1.Determined.PatchModel:output_type -> determined.api.v1.PatchModelResponse + 295, // 295: determined.api.v1.Determined.ArchiveModel:output_type -> determined.api.v1.ArchiveModelResponse + 296, // 296: determined.api.v1.Determined.UnarchiveModel:output_type -> determined.api.v1.UnarchiveModelResponse + 297, // 297: determined.api.v1.Determined.MoveModel:output_type -> determined.api.v1.MoveModelResponse + 298, // 298: determined.api.v1.Determined.DeleteModel:output_type -> determined.api.v1.DeleteModelResponse + 299, // 299: determined.api.v1.Determined.GetModels:output_type -> determined.api.v1.GetModelsResponse + 300, // 300: determined.api.v1.Determined.GetModelLabels:output_type -> determined.api.v1.GetModelLabelsResponse + 301, // 301: determined.api.v1.Determined.GetModelVersion:output_type -> determined.api.v1.GetModelVersionResponse + 302, // 302: determined.api.v1.Determined.GetModelVersions:output_type -> determined.api.v1.GetModelVersionsResponse + 303, // 303: determined.api.v1.Determined.PostModelVersion:output_type -> determined.api.v1.PostModelVersionResponse + 304, // 304: determined.api.v1.Determined.PatchModelVersion:output_type -> determined.api.v1.PatchModelVersionResponse + 305, // 305: determined.api.v1.Determined.DeleteModelVersion:output_type -> determined.api.v1.DeleteModelVersionResponse + 306, // 306: determined.api.v1.Determined.GetCheckpoint:output_type -> determined.api.v1.GetCheckpointResponse + 307, // 307: determined.api.v1.Determined.PostCheckpointMetadata:output_type -> determined.api.v1.PostCheckpointMetadataResponse + 308, // 308: determined.api.v1.Determined.DeleteCheckpoints:output_type -> determined.api.v1.DeleteCheckpointsResponse + 309, // 309: determined.api.v1.Determined.GetSearcherEvents:output_type -> determined.api.v1.GetSearcherEventsResponse + 310, // 310: determined.api.v1.Determined.PostSearcherOperations:output_type -> determined.api.v1.PostSearcherOperationsResponse + 311, // 311: determined.api.v1.Determined.MetricNames:output_type -> determined.api.v1.MetricNamesResponse + 312, // 312: determined.api.v1.Determined.MetricBatches:output_type -> determined.api.v1.MetricBatchesResponse + 313, // 313: determined.api.v1.Determined.TrialsSnapshot:output_type -> determined.api.v1.TrialsSnapshotResponse + 314, // 314: determined.api.v1.Determined.TrialsSample:output_type -> determined.api.v1.TrialsSampleResponse + 315, // 315: determined.api.v1.Determined.GetResourcePools:output_type -> determined.api.v1.GetResourcePoolsResponse + 316, // 316: determined.api.v1.Determined.ComputeHPImportance:output_type -> determined.api.v1.ComputeHPImportanceResponse + 317, // 317: determined.api.v1.Determined.GetHPImportance:output_type -> determined.api.v1.GetHPImportanceResponse + 318, // 318: determined.api.v1.Determined.ResourceAllocationRaw:output_type -> determined.api.v1.ResourceAllocationRawResponse + 319, // 319: determined.api.v1.Determined.ResourceAllocationAggregated:output_type -> determined.api.v1.ResourceAllocationAggregatedResponse + 320, // 320: determined.api.v1.Determined.GetWorkspace:output_type -> determined.api.v1.GetWorkspaceResponse + 321, // 321: determined.api.v1.Determined.GetWorkspaceProjects:output_type -> determined.api.v1.GetWorkspaceProjectsResponse + 322, // 322: determined.api.v1.Determined.GetWorkspaces:output_type -> determined.api.v1.GetWorkspacesResponse + 323, // 323: determined.api.v1.Determined.PostWorkspace:output_type -> determined.api.v1.PostWorkspaceResponse + 324, // 324: determined.api.v1.Determined.PatchWorkspace:output_type -> determined.api.v1.PatchWorkspaceResponse + 325, // 325: determined.api.v1.Determined.DeleteWorkspace:output_type -> determined.api.v1.DeleteWorkspaceResponse + 326, // 326: determined.api.v1.Determined.ArchiveWorkspace:output_type -> determined.api.v1.ArchiveWorkspaceResponse + 327, // 327: determined.api.v1.Determined.UnarchiveWorkspace:output_type -> determined.api.v1.UnarchiveWorkspaceResponse + 328, // 328: determined.api.v1.Determined.PinWorkspace:output_type -> determined.api.v1.PinWorkspaceResponse + 329, // 329: determined.api.v1.Determined.UnpinWorkspace:output_type -> determined.api.v1.UnpinWorkspaceResponse + 330, // 330: determined.api.v1.Determined.GetProject:output_type -> determined.api.v1.GetProjectResponse + 331, // 331: determined.api.v1.Determined.PostProject:output_type -> determined.api.v1.PostProjectResponse + 332, // 332: determined.api.v1.Determined.AddProjectNote:output_type -> determined.api.v1.AddProjectNoteResponse + 333, // 333: determined.api.v1.Determined.PutProjectNotes:output_type -> determined.api.v1.PutProjectNotesResponse + 334, // 334: determined.api.v1.Determined.PatchProject:output_type -> determined.api.v1.PatchProjectResponse + 335, // 335: determined.api.v1.Determined.DeleteProject:output_type -> determined.api.v1.DeleteProjectResponse + 336, // 336: determined.api.v1.Determined.ArchiveProject:output_type -> determined.api.v1.ArchiveProjectResponse + 337, // 337: determined.api.v1.Determined.UnarchiveProject:output_type -> determined.api.v1.UnarchiveProjectResponse + 338, // 338: determined.api.v1.Determined.MoveProject:output_type -> determined.api.v1.MoveProjectResponse + 339, // 339: determined.api.v1.Determined.MoveExperiment:output_type -> determined.api.v1.MoveExperimentResponse + 340, // 340: determined.api.v1.Determined.GetWebhooks:output_type -> determined.api.v1.GetWebhooksResponse + 341, // 341: determined.api.v1.Determined.PostWebhook:output_type -> determined.api.v1.PostWebhookResponse + 342, // 342: determined.api.v1.Determined.DeleteWebhook:output_type -> determined.api.v1.DeleteWebhookResponse + 343, // 343: determined.api.v1.Determined.TestWebhook:output_type -> determined.api.v1.TestWebhookResponse + 344, // 344: determined.api.v1.Determined.GetGroup:output_type -> determined.api.v1.GetGroupResponse + 345, // 345: determined.api.v1.Determined.GetGroups:output_type -> determined.api.v1.GetGroupsResponse + 346, // 346: determined.api.v1.Determined.CreateGroup:output_type -> determined.api.v1.CreateGroupResponse + 347, // 347: determined.api.v1.Determined.UpdateGroup:output_type -> determined.api.v1.UpdateGroupResponse + 348, // 348: determined.api.v1.Determined.DeleteGroup:output_type -> determined.api.v1.DeleteGroupResponse + 349, // 349: determined.api.v1.Determined.GetPermissionsSummary:output_type -> determined.api.v1.GetPermissionsSummaryResponse + 350, // 350: determined.api.v1.Determined.GetGroupsAndUsersAssignedToWorkspace:output_type -> determined.api.v1.GetGroupsAndUsersAssignedToWorkspaceResponse + 351, // 351: determined.api.v1.Determined.GetRolesByID:output_type -> determined.api.v1.GetRolesByIDResponse + 352, // 352: determined.api.v1.Determined.GetRolesAssignedToUser:output_type -> determined.api.v1.GetRolesAssignedToUserResponse + 353, // 353: determined.api.v1.Determined.GetRolesAssignedToGroup:output_type -> determined.api.v1.GetRolesAssignedToGroupResponse + 354, // 354: determined.api.v1.Determined.SearchRolesAssignableToScope:output_type -> determined.api.v1.SearchRolesAssignableToScopeResponse + 355, // 355: determined.api.v1.Determined.ListRoles:output_type -> determined.api.v1.ListRolesResponse + 356, // 356: determined.api.v1.Determined.AssignRoles:output_type -> determined.api.v1.AssignRolesResponse + 357, // 357: determined.api.v1.Determined.RemoveAssignments:output_type -> determined.api.v1.RemoveAssignmentsResponse + 358, // 358: determined.api.v1.Determined.PostUserActivity:output_type -> determined.api.v1.PostUserActivityResponse + 359, // 359: determined.api.v1.Determined.GetProjectsByUserActivity:output_type -> determined.api.v1.GetProjectsByUserActivityResponse + 180, // [180:360] is the sub-list for method output_type + 0, // [0:180] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -3136,8 +3105,6 @@ type DeterminedClient interface { PostSearcherOperations(ctx context.Context, in *PostSearcherOperationsRequest, opts ...grpc.CallOption) (*PostSearcherOperationsResponse, error) // Get the set of metric names recorded for an experiment. MetricNames(ctx context.Context, in *MetricNamesRequest, opts ...grpc.CallOption) (Determined_MetricNamesClient, error) - // Get the set of metric names recorded for a trial. - ExpCompareMetricNames(ctx context.Context, in *ExpCompareMetricNamesRequest, opts ...grpc.CallOption) (Determined_ExpCompareMetricNamesClient, error) // Get the milestones (in batches processed) at which a metric is recorded by // an experiment. MetricBatches(ctx context.Context, in *MetricBatchesRequest, opts ...grpc.CallOption) (Determined_MetricBatchesClient, error) @@ -3146,8 +3113,6 @@ type DeterminedClient interface { TrialsSnapshot(ctx context.Context, in *TrialsSnapshotRequest, opts ...grpc.CallOption) (Determined_TrialsSnapshotClient, error) // Get a sample of the metrics over time for a sample of the trials. TrialsSample(ctx context.Context, in *TrialsSampleRequest, opts ...grpc.CallOption) (Determined_TrialsSampleClient, error) - // Get a sample of the metrics over time for a sample of the trials. - ExpCompareTrialsSample(ctx context.Context, in *ExpCompareTrialsSampleRequest, opts ...grpc.CallOption) (Determined_ExpCompareTrialsSampleClient, error) // Get a list of all resource pools from the cluster. GetResourcePools(ctx context.Context, in *GetResourcePoolsRequest, opts ...grpc.CallOption) (*GetResourcePoolsResponse, error) // Trigger the computation of hyperparameter importance on-demand for a @@ -4630,40 +4595,8 @@ func (x *determinedMetricNamesClient) Recv() (*MetricNamesResponse, error) { return m, nil } -func (c *determinedClient) ExpCompareMetricNames(ctx context.Context, in *ExpCompareMetricNamesRequest, opts ...grpc.CallOption) (Determined_ExpCompareMetricNamesClient, error) { - stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[8], "/determined.api.v1.Determined/ExpCompareMetricNames", opts...) - if err != nil { - return nil, err - } - x := &determinedExpCompareMetricNamesClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Determined_ExpCompareMetricNamesClient interface { - Recv() (*ExpCompareMetricNamesResponse, error) - grpc.ClientStream -} - -type determinedExpCompareMetricNamesClient struct { - grpc.ClientStream -} - -func (x *determinedExpCompareMetricNamesClient) Recv() (*ExpCompareMetricNamesResponse, error) { - m := new(ExpCompareMetricNamesResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - func (c *determinedClient) MetricBatches(ctx context.Context, in *MetricBatchesRequest, opts ...grpc.CallOption) (Determined_MetricBatchesClient, error) { - stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[9], "/determined.api.v1.Determined/MetricBatches", opts...) + stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[8], "/determined.api.v1.Determined/MetricBatches", opts...) if err != nil { return nil, err } @@ -4695,7 +4628,7 @@ func (x *determinedMetricBatchesClient) Recv() (*MetricBatchesResponse, error) { } func (c *determinedClient) TrialsSnapshot(ctx context.Context, in *TrialsSnapshotRequest, opts ...grpc.CallOption) (Determined_TrialsSnapshotClient, error) { - stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[10], "/determined.api.v1.Determined/TrialsSnapshot", opts...) + stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[9], "/determined.api.v1.Determined/TrialsSnapshot", opts...) if err != nil { return nil, err } @@ -4727,7 +4660,7 @@ func (x *determinedTrialsSnapshotClient) Recv() (*TrialsSnapshotResponse, error) } func (c *determinedClient) TrialsSample(ctx context.Context, in *TrialsSampleRequest, opts ...grpc.CallOption) (Determined_TrialsSampleClient, error) { - stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[11], "/determined.api.v1.Determined/TrialsSample", opts...) + stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[10], "/determined.api.v1.Determined/TrialsSample", opts...) if err != nil { return nil, err } @@ -4758,38 +4691,6 @@ func (x *determinedTrialsSampleClient) Recv() (*TrialsSampleResponse, error) { return m, nil } -func (c *determinedClient) ExpCompareTrialsSample(ctx context.Context, in *ExpCompareTrialsSampleRequest, opts ...grpc.CallOption) (Determined_ExpCompareTrialsSampleClient, error) { - stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[12], "/determined.api.v1.Determined/ExpCompareTrialsSample", opts...) - if err != nil { - return nil, err - } - x := &determinedExpCompareTrialsSampleClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Determined_ExpCompareTrialsSampleClient interface { - Recv() (*ExpCompareTrialsSampleResponse, error) - grpc.ClientStream -} - -type determinedExpCompareTrialsSampleClient struct { - grpc.ClientStream -} - -func (x *determinedExpCompareTrialsSampleClient) Recv() (*ExpCompareTrialsSampleResponse, error) { - m := new(ExpCompareTrialsSampleResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - func (c *determinedClient) GetResourcePools(ctx context.Context, in *GetResourcePoolsRequest, opts ...grpc.CallOption) (*GetResourcePoolsResponse, error) { out := new(GetResourcePoolsResponse) err := c.cc.Invoke(ctx, "/determined.api.v1.Determined/GetResourcePools", in, out, opts...) @@ -4809,7 +4710,7 @@ func (c *determinedClient) ComputeHPImportance(ctx context.Context, in *ComputeH } func (c *determinedClient) GetHPImportance(ctx context.Context, in *GetHPImportanceRequest, opts ...grpc.CallOption) (Determined_GetHPImportanceClient, error) { - stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[13], "/determined.api.v1.Determined/GetHPImportance", opts...) + stream, err := c.cc.NewStream(ctx, &_Determined_serviceDesc.Streams[11], "/determined.api.v1.Determined/GetHPImportance", opts...) if err != nil { return nil, err } @@ -5515,8 +5416,6 @@ type DeterminedServer interface { PostSearcherOperations(context.Context, *PostSearcherOperationsRequest) (*PostSearcherOperationsResponse, error) // Get the set of metric names recorded for an experiment. MetricNames(*MetricNamesRequest, Determined_MetricNamesServer) error - // Get the set of metric names recorded for a trial. - ExpCompareMetricNames(*ExpCompareMetricNamesRequest, Determined_ExpCompareMetricNamesServer) error // Get the milestones (in batches processed) at which a metric is recorded by // an experiment. MetricBatches(*MetricBatchesRequest, Determined_MetricBatchesServer) error @@ -5525,8 +5424,6 @@ type DeterminedServer interface { TrialsSnapshot(*TrialsSnapshotRequest, Determined_TrialsSnapshotServer) error // Get a sample of the metrics over time for a sample of the trials. TrialsSample(*TrialsSampleRequest, Determined_TrialsSampleServer) error - // Get a sample of the metrics over time for a sample of the trials. - ExpCompareTrialsSample(*ExpCompareTrialsSampleRequest, Determined_ExpCompareTrialsSampleServer) error // Get a list of all resource pools from the cluster. GetResourcePools(context.Context, *GetResourcePoolsRequest) (*GetResourcePoolsResponse, error) // Trigger the computation of hyperparameter importance on-demand for a @@ -6027,9 +5924,6 @@ func (*UnimplementedDeterminedServer) PostSearcherOperations(context.Context, *P func (*UnimplementedDeterminedServer) MetricNames(*MetricNamesRequest, Determined_MetricNamesServer) error { return status.Errorf(codes.Unimplemented, "method MetricNames not implemented") } -func (*UnimplementedDeterminedServer) ExpCompareMetricNames(*ExpCompareMetricNamesRequest, Determined_ExpCompareMetricNamesServer) error { - return status.Errorf(codes.Unimplemented, "method ExpCompareMetricNames not implemented") -} func (*UnimplementedDeterminedServer) MetricBatches(*MetricBatchesRequest, Determined_MetricBatchesServer) error { return status.Errorf(codes.Unimplemented, "method MetricBatches not implemented") } @@ -6039,9 +5933,6 @@ func (*UnimplementedDeterminedServer) TrialsSnapshot(*TrialsSnapshotRequest, Det func (*UnimplementedDeterminedServer) TrialsSample(*TrialsSampleRequest, Determined_TrialsSampleServer) error { return status.Errorf(codes.Unimplemented, "method TrialsSample not implemented") } -func (*UnimplementedDeterminedServer) ExpCompareTrialsSample(*ExpCompareTrialsSampleRequest, Determined_ExpCompareTrialsSampleServer) error { - return status.Errorf(codes.Unimplemented, "method ExpCompareTrialsSample not implemented") -} func (*UnimplementedDeterminedServer) GetResourcePools(context.Context, *GetResourcePoolsRequest) (*GetResourcePoolsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetResourcePools not implemented") } @@ -8582,27 +8473,6 @@ func (x *determinedMetricNamesServer) Send(m *MetricNamesResponse) error { return x.ServerStream.SendMsg(m) } -func _Determined_ExpCompareMetricNames_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ExpCompareMetricNamesRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(DeterminedServer).ExpCompareMetricNames(m, &determinedExpCompareMetricNamesServer{stream}) -} - -type Determined_ExpCompareMetricNamesServer interface { - Send(*ExpCompareMetricNamesResponse) error - grpc.ServerStream -} - -type determinedExpCompareMetricNamesServer struct { - grpc.ServerStream -} - -func (x *determinedExpCompareMetricNamesServer) Send(m *ExpCompareMetricNamesResponse) error { - return x.ServerStream.SendMsg(m) -} - func _Determined_MetricBatches_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(MetricBatchesRequest) if err := stream.RecvMsg(m); err != nil { @@ -8666,27 +8536,6 @@ func (x *determinedTrialsSampleServer) Send(m *TrialsSampleResponse) error { return x.ServerStream.SendMsg(m) } -func _Determined_ExpCompareTrialsSample_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ExpCompareTrialsSampleRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(DeterminedServer).ExpCompareTrialsSample(m, &determinedExpCompareTrialsSampleServer{stream}) -} - -type Determined_ExpCompareTrialsSampleServer interface { - Send(*ExpCompareTrialsSampleResponse) error - grpc.ServerStream -} - -type determinedExpCompareTrialsSampleServer struct { - grpc.ServerStream -} - -func (x *determinedExpCompareTrialsSampleServer) Send(m *ExpCompareTrialsSampleResponse) error { - return x.ServerStream.SendMsg(m) -} - func _Determined_GetResourcePools_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetResourcePoolsRequest) if err := dec(in); err != nil { @@ -10218,11 +10067,6 @@ var _Determined_serviceDesc = grpc.ServiceDesc{ Handler: _Determined_MetricNames_Handler, ServerStreams: true, }, - { - StreamName: "ExpCompareMetricNames", - Handler: _Determined_ExpCompareMetricNames_Handler, - ServerStreams: true, - }, { StreamName: "MetricBatches", Handler: _Determined_MetricBatches_Handler, @@ -10238,11 +10082,6 @@ var _Determined_serviceDesc = grpc.ServiceDesc{ Handler: _Determined_TrialsSample_Handler, ServerStreams: true, }, - { - StreamName: "ExpCompareTrialsSample", - Handler: _Determined_ExpCompareTrialsSample_Handler, - ServerStreams: true, - }, { StreamName: "GetHPImportance", Handler: _Determined_GetHPImportance_Handler, diff --git a/proto/pkg/apiv1/api.pb.gw.go b/proto/pkg/apiv1/api.pb.gw.go index fafd2dca304..13ed6ce7e27 100644 --- a/proto/pkg/apiv1/api.pb.gw.go +++ b/proto/pkg/apiv1/api.pb.gw.go @@ -6903,34 +6903,6 @@ func request_Determined_MetricNames_0(ctx context.Context, marshaler runtime.Mar } -var ( - filter_Determined_ExpCompareMetricNames_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - -func request_Determined_ExpCompareMetricNames_0(ctx context.Context, marshaler runtime.Marshaler, client DeterminedClient, req *http.Request, pathParams map[string]string) (Determined_ExpCompareMetricNamesClient, runtime.ServerMetadata, error) { - var protoReq ExpCompareMetricNamesRequest - var metadata runtime.ServerMetadata - - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Determined_ExpCompareMetricNames_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - stream, err := client.ExpCompareMetricNames(ctx, &protoReq) - if err != nil { - return nil, metadata, err - } - header, err := stream.Header() - if err != nil { - return nil, metadata, err - } - metadata.HeaderMD = header - return stream, metadata, nil - -} - var ( filter_Determined_MetricBatches_0 = &utilities.DoubleArray{Encoding: map[string]int{"experiment_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) @@ -7069,34 +7041,6 @@ func request_Determined_TrialsSample_0(ctx context.Context, marshaler runtime.Ma } -var ( - filter_Determined_ExpCompareTrialsSample_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - -func request_Determined_ExpCompareTrialsSample_0(ctx context.Context, marshaler runtime.Marshaler, client DeterminedClient, req *http.Request, pathParams map[string]string) (Determined_ExpCompareTrialsSampleClient, runtime.ServerMetadata, error) { - var protoReq ExpCompareTrialsSampleRequest - var metadata runtime.ServerMetadata - - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Determined_ExpCompareTrialsSample_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - stream, err := client.ExpCompareTrialsSample(ctx, &protoReq) - if err != nil { - return nil, metadata, err - } - header, err := stream.Header() - if err != nil { - return nil, metadata, err - } - metadata.HeaderMD = header - return stream, metadata, nil - -} - var ( filter_Determined_GetResourcePools_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -11880,13 +11824,6 @@ func RegisterDeterminedHandlerServer(ctx context.Context, mux *runtime.ServeMux, return }) - mux.Handle("GET", pattern_Determined_ExpCompareMetricNames_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") - _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - }) - mux.Handle("GET", pattern_Determined_MetricBatches_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -11908,13 +11845,6 @@ func RegisterDeterminedHandlerServer(ctx context.Context, mux *runtime.ServeMux, return }) - mux.Handle("GET", pattern_Determined_ExpCompareTrialsSample_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") - _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - }) - mux.Handle("GET", pattern_Determined_GetResourcePools_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -15483,26 +15413,6 @@ func RegisterDeterminedHandlerClient(ctx context.Context, mux *runtime.ServeMux, }) - mux.Handle("GET", pattern_Determined_ExpCompareMetricNames_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Determined_ExpCompareMetricNames_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Determined_ExpCompareMetricNames_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Determined_MetricBatches_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -15563,26 +15473,6 @@ func RegisterDeterminedHandlerClient(ctx context.Context, mux *runtime.ServeMux, }) - mux.Handle("GET", pattern_Determined_ExpCompareTrialsSample_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Determined_ExpCompareTrialsSample_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Determined_ExpCompareTrialsSample_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Determined_GetResourcePools_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -16751,16 +16641,12 @@ var ( pattern_Determined_MetricNames_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 2, 5}, []string{"api", "v1", "experiments", "experiment_id", "metrics-stream", "metric-names"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_Determined_ExpCompareMetricNames_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "trials", "metrics-stream", "metric-names"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_Determined_MetricBatches_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 2, 5}, []string{"api", "v1", "experiments", "experiment_id", "metrics-stream", "batches"}, "", runtime.AssumeColonVerbOpt(true))) pattern_Determined_TrialsSnapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 2, 5}, []string{"api", "v1", "experiments", "experiment_id", "metrics-stream", "trials-snapshot"}, "", runtime.AssumeColonVerbOpt(true))) pattern_Determined_TrialsSample_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 2, 5}, []string{"api", "v1", "experiments", "experiment_id", "metrics-stream", "trials-sample"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_Determined_ExpCompareTrialsSample_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "experiments-compare"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_Determined_GetResourcePools_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "resource-pools"}, "", runtime.AssumeColonVerbOpt(true))) pattern_Determined_ComputeHPImportance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "experiments", "experiment_id", "hyperparameter-importance"}, "", runtime.AssumeColonVerbOpt(true))) @@ -17117,16 +17003,12 @@ var ( forward_Determined_MetricNames_0 = runtime.ForwardResponseStream - forward_Determined_ExpCompareMetricNames_0 = runtime.ForwardResponseStream - forward_Determined_MetricBatches_0 = runtime.ForwardResponseStream forward_Determined_TrialsSnapshot_0 = runtime.ForwardResponseStream forward_Determined_TrialsSample_0 = runtime.ForwardResponseStream - forward_Determined_ExpCompareTrialsSample_0 = runtime.ForwardResponseStream - forward_Determined_GetResourcePools_0 = runtime.ForwardResponseMessage forward_Determined_ComputeHPImportance_0 = runtime.ForwardResponseMessage diff --git a/proto/pkg/apiv1/experiment.pb.go b/proto/pkg/apiv1/experiment.pb.go index f0306fd7faf..cf4985be5a5 100644 --- a/proto/pkg/apiv1/experiment.pb.go +++ b/proto/pkg/apiv1/experiment.pb.go @@ -320,7 +320,7 @@ type DataPointTime struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Total batches processed by the time this measurement is taken. + // The time the measurement is taken. Time *timestamp.Timestamp `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` // Value of the requested metric at this point in the trial. Value float64 `protobuf:"fixed64,2,opt,name=value,proto3" json:"value,omitempty"` @@ -2373,122 +2373,6 @@ func (x *MetricNamesResponse) GetValidationMetrics() []string { return nil } -// Request for the set of metrics recorded by an experiment. -type ExpCompareMetricNamesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The id of the experiment. - TrialId []int32 `protobuf:"varint,1,rep,packed,name=trial_id,json=trialId,proto3" json:"trial_id,omitempty"` - // Seconds to wait when polling for updates. - PeriodSeconds int32 `protobuf:"varint,2,opt,name=period_seconds,json=periodSeconds,proto3" json:"period_seconds,omitempty"` -} - -func (x *ExpCompareMetricNamesRequest) Reset() { - *x = ExpCompareMetricNamesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExpCompareMetricNamesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExpCompareMetricNamesRequest) ProtoMessage() {} - -func (x *ExpCompareMetricNamesRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[37] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExpCompareMetricNamesRequest.ProtoReflect.Descriptor instead. -func (*ExpCompareMetricNamesRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{37} -} - -func (x *ExpCompareMetricNamesRequest) GetTrialId() []int32 { - if x != nil { - return x.TrialId - } - return nil -} - -func (x *ExpCompareMetricNamesRequest) GetPeriodSeconds() int32 { - if x != nil { - return x.PeriodSeconds - } - return 0 -} - -// Response to MetricNamesRequest. -type ExpCompareMetricNamesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // List of training metric names. - TrainingMetrics []string `protobuf:"bytes,1,rep,name=training_metrics,json=trainingMetrics,proto3" json:"training_metrics,omitempty"` - // List of validation metric names. - ValidationMetrics []string `protobuf:"bytes,2,rep,name=validation_metrics,json=validationMetrics,proto3" json:"validation_metrics,omitempty"` -} - -func (x *ExpCompareMetricNamesResponse) Reset() { - *x = ExpCompareMetricNamesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExpCompareMetricNamesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExpCompareMetricNamesResponse) ProtoMessage() {} - -func (x *ExpCompareMetricNamesResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[38] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExpCompareMetricNamesResponse.ProtoReflect.Descriptor instead. -func (*ExpCompareMetricNamesResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{38} -} - -func (x *ExpCompareMetricNamesResponse) GetTrainingMetrics() []string { - if x != nil { - return x.TrainingMetrics - } - return nil -} - -func (x *ExpCompareMetricNamesResponse) GetValidationMetrics() []string { - if x != nil { - return x.ValidationMetrics - } - return nil -} - // Request the milestones (in batches processed) at which a metric is recorded // by an experiment. type MetricBatchesRequest struct { @@ -2509,7 +2393,7 @@ type MetricBatchesRequest struct { func (x *MetricBatchesRequest) Reset() { *x = MetricBatchesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[39] + mi := &file_determined_api_v1_experiment_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2522,7 +2406,7 @@ func (x *MetricBatchesRequest) String() string { func (*MetricBatchesRequest) ProtoMessage() {} func (x *MetricBatchesRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[39] + mi := &file_determined_api_v1_experiment_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2535,7 +2419,7 @@ func (x *MetricBatchesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MetricBatchesRequest.ProtoReflect.Descriptor instead. func (*MetricBatchesRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{39} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{37} } func (x *MetricBatchesRequest) GetExperimentId() int32 { @@ -2580,7 +2464,7 @@ type MetricBatchesResponse struct { func (x *MetricBatchesResponse) Reset() { *x = MetricBatchesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[40] + mi := &file_determined_api_v1_experiment_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2593,7 +2477,7 @@ func (x *MetricBatchesResponse) String() string { func (*MetricBatchesResponse) ProtoMessage() {} func (x *MetricBatchesResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[40] + mi := &file_determined_api_v1_experiment_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2606,7 +2490,7 @@ func (x *MetricBatchesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MetricBatchesResponse.ProtoReflect.Descriptor instead. func (*MetricBatchesResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{40} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{38} } func (x *MetricBatchesResponse) GetBatches() []int32 { @@ -2616,7 +2500,7 @@ func (x *MetricBatchesResponse) GetBatches() []int32 { return nil } -// Request metrics from all trials at a progress point of progress. +// Request metrics from all trials at a point of progress. type TrialsSnapshotRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2639,7 +2523,7 @@ type TrialsSnapshotRequest struct { func (x *TrialsSnapshotRequest) Reset() { *x = TrialsSnapshotRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[41] + mi := &file_determined_api_v1_experiment_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2652,7 +2536,7 @@ func (x *TrialsSnapshotRequest) String() string { func (*TrialsSnapshotRequest) ProtoMessage() {} func (x *TrialsSnapshotRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[41] + mi := &file_determined_api_v1_experiment_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2665,7 +2549,7 @@ func (x *TrialsSnapshotRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TrialsSnapshotRequest.ProtoReflect.Descriptor instead. func (*TrialsSnapshotRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{41} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{39} } func (x *TrialsSnapshotRequest) GetExperimentId() int32 { @@ -2723,7 +2607,7 @@ type TrialsSnapshotResponse struct { func (x *TrialsSnapshotResponse) Reset() { *x = TrialsSnapshotResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[42] + mi := &file_determined_api_v1_experiment_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2736,7 +2620,7 @@ func (x *TrialsSnapshotResponse) String() string { func (*TrialsSnapshotResponse) ProtoMessage() {} func (x *TrialsSnapshotResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[42] + mi := &file_determined_api_v1_experiment_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2749,7 +2633,7 @@ func (x *TrialsSnapshotResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TrialsSnapshotResponse.ProtoReflect.Descriptor instead. func (*TrialsSnapshotResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{42} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{40} } func (x *TrialsSnapshotResponse) GetTrials() []*TrialsSnapshotResponse_Trial { @@ -2786,7 +2670,7 @@ type TrialsSampleRequest struct { func (x *TrialsSampleRequest) Reset() { *x = TrialsSampleRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[43] + mi := &file_determined_api_v1_experiment_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2799,7 +2683,7 @@ func (x *TrialsSampleRequest) String() string { func (*TrialsSampleRequest) ProtoMessage() {} func (x *TrialsSampleRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[43] + mi := &file_determined_api_v1_experiment_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2812,7 +2696,7 @@ func (x *TrialsSampleRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TrialsSampleRequest.ProtoReflect.Descriptor instead. func (*TrialsSampleRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{43} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{41} } func (x *TrialsSampleRequest) GetExperimentId() int32 { @@ -2888,7 +2772,7 @@ type TrialsSampleResponse struct { func (x *TrialsSampleResponse) Reset() { *x = TrialsSampleResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[44] + mi := &file_determined_api_v1_experiment_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2901,7 +2785,7 @@ func (x *TrialsSampleResponse) String() string { func (*TrialsSampleResponse) ProtoMessage() {} func (x *TrialsSampleResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[44] + mi := &file_determined_api_v1_experiment_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2914,7 +2798,7 @@ func (x *TrialsSampleResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TrialsSampleResponse.ProtoReflect.Descriptor instead. func (*TrialsSampleResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{44} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{42} } func (x *TrialsSampleResponse) GetTrials() []*TrialsSampleResponse_Trial { @@ -2938,47 +2822,38 @@ func (x *TrialsSampleResponse) GetDemotedTrials() []int32 { return nil } -// Request a sample of metrics over time for a sample of trials. -type ExpCompareTrialsSampleRequest struct { +// Trigger the computation of hyperparameter importance on-demand for a specific +// metric on a specific experiment. +type ComputeHPImportanceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The id of the experiment. - ExperimentIds []int32 `protobuf:"varint,1,rep,packed,name=experiment_ids,json=experimentIds,proto3" json:"experiment_ids,omitempty"` + ExperimentId int32 `protobuf:"varint,1,opt,name=experiment_id,json=experimentId,proto3" json:"experiment_id,omitempty"` // A metric name. MetricName string `protobuf:"bytes,2,opt,name=metric_name,json=metricName,proto3" json:"metric_name,omitempty"` // The type of metric. MetricType MetricType `protobuf:"varint,3,opt,name=metric_type,json=metricType,proto3,enum=determined.api.v1.MetricType" json:"metric_type,omitempty"` - // Maximum number of trials to fetch data for. - MaxTrials int32 `protobuf:"varint,4,opt,name=max_trials,json=maxTrials,proto3" json:"max_trials,omitempty"` - // Maximum number of initial / historical data points. - MaxDatapoints int32 `protobuf:"varint,5,opt,name=max_datapoints,json=maxDatapoints,proto3" json:"max_datapoints,omitempty"` - // Beginning of window (inclusive) to fetch data for. - StartBatches int32 `protobuf:"varint,6,opt,name=start_batches,json=startBatches,proto3" json:"start_batches,omitempty"` - // Ending of window (inclusive) to fetch data for. - EndBatches int32 `protobuf:"varint,7,opt,name=end_batches,json=endBatches,proto3" json:"end_batches,omitempty"` - // Seconds to wait when polling for updates. - PeriodSeconds int32 `protobuf:"varint,8,opt,name=period_seconds,json=periodSeconds,proto3" json:"period_seconds,omitempty"` } -func (x *ExpCompareTrialsSampleRequest) Reset() { - *x = ExpCompareTrialsSampleRequest{} +func (x *ComputeHPImportanceRequest) Reset() { + *x = ComputeHPImportanceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[45] + mi := &file_determined_api_v1_experiment_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ExpCompareTrialsSampleRequest) String() string { +func (x *ComputeHPImportanceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExpCompareTrialsSampleRequest) ProtoMessage() {} +func (*ComputeHPImportanceRequest) ProtoMessage() {} -func (x *ExpCompareTrialsSampleRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[45] +func (x *ComputeHPImportanceRequest) ProtoReflect() protoreflect.Message { + mi := &file_determined_api_v1_experiment_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2989,98 +2864,100 @@ func (x *ExpCompareTrialsSampleRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExpCompareTrialsSampleRequest.ProtoReflect.Descriptor instead. -func (*ExpCompareTrialsSampleRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{45} +// Deprecated: Use ComputeHPImportanceRequest.ProtoReflect.Descriptor instead. +func (*ComputeHPImportanceRequest) Descriptor() ([]byte, []int) { + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{43} } -func (x *ExpCompareTrialsSampleRequest) GetExperimentIds() []int32 { +func (x *ComputeHPImportanceRequest) GetExperimentId() int32 { if x != nil { - return x.ExperimentIds + return x.ExperimentId } - return nil + return 0 } -func (x *ExpCompareTrialsSampleRequest) GetMetricName() string { +func (x *ComputeHPImportanceRequest) GetMetricName() string { if x != nil { return x.MetricName } return "" } -func (x *ExpCompareTrialsSampleRequest) GetMetricType() MetricType { +func (x *ComputeHPImportanceRequest) GetMetricType() MetricType { if x != nil { return x.MetricType } return MetricType_METRIC_TYPE_UNSPECIFIED } -func (x *ExpCompareTrialsSampleRequest) GetMaxTrials() int32 { - if x != nil { - return x.MaxTrials - } - return 0 +// Response to ComputeHPImportanceRequest +type ComputeHPImportanceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (x *ExpCompareTrialsSampleRequest) GetMaxDatapoints() int32 { - if x != nil { - return x.MaxDatapoints +func (x *ComputeHPImportanceResponse) Reset() { + *x = ComputeHPImportanceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_determined_api_v1_experiment_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *ExpCompareTrialsSampleRequest) GetStartBatches() int32 { - if x != nil { - return x.StartBatches - } - return 0 +func (x *ComputeHPImportanceResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *ExpCompareTrialsSampleRequest) GetEndBatches() int32 { - if x != nil { - return x.EndBatches +func (*ComputeHPImportanceResponse) ProtoMessage() {} + +func (x *ComputeHPImportanceResponse) ProtoReflect() protoreflect.Message { + mi := &file_determined_api_v1_experiment_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -func (x *ExpCompareTrialsSampleRequest) GetPeriodSeconds() int32 { - if x != nil { - return x.PeriodSeconds - } - return 0 +// Deprecated: Use ComputeHPImportanceResponse.ProtoReflect.Descriptor instead. +func (*ComputeHPImportanceResponse) Descriptor() ([]byte, []int) { + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{44} } -// Response to ExpCompareTrialsSampleRequest -type ExpCompareTrialsSampleResponse struct { +// Retrieve the status and results of hyperparameter importance computation. +type GetHPImportanceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // A historical or incremental series of data points for the trials. - Trials []*ExpCompareTrialsSampleResponse_ExpTrial `protobuf:"bytes,1,rep,name=trials,proto3" json:"trials,omitempty"` - // IDs of trials that are newly included in the data. - PromotedTrials []int32 `protobuf:"varint,2,rep,packed,name=promoted_trials,json=promotedTrials,proto3" json:"promoted_trials,omitempty"` - // IDs of trials that are no loger included in the top N trials. - DemotedTrials []int32 `protobuf:"varint,3,rep,packed,name=demoted_trials,json=demotedTrials,proto3" json:"demoted_trials,omitempty"` + // The id of the experiment. + ExperimentId int32 `protobuf:"varint,1,opt,name=experiment_id,json=experimentId,proto3" json:"experiment_id,omitempty"` + // Seconds to wait when polling for updates. + PeriodSeconds int32 `protobuf:"varint,2,opt,name=period_seconds,json=periodSeconds,proto3" json:"period_seconds,omitempty"` } -func (x *ExpCompareTrialsSampleResponse) Reset() { - *x = ExpCompareTrialsSampleResponse{} +func (x *GetHPImportanceRequest) Reset() { + *x = GetHPImportanceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[46] + mi := &file_determined_api_v1_experiment_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ExpCompareTrialsSampleResponse) String() string { +func (x *GetHPImportanceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExpCompareTrialsSampleResponse) ProtoMessage() {} +func (*GetHPImportanceRequest) ProtoMessage() {} -func (x *ExpCompareTrialsSampleResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[46] +func (x *GetHPImportanceRequest) ProtoReflect() protoreflect.Message { + mi := &file_determined_api_v1_experiment_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3091,199 +2968,27 @@ func (x *ExpCompareTrialsSampleResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExpCompareTrialsSampleResponse.ProtoReflect.Descriptor instead. -func (*ExpCompareTrialsSampleResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{46} -} - -func (x *ExpCompareTrialsSampleResponse) GetTrials() []*ExpCompareTrialsSampleResponse_ExpTrial { - if x != nil { - return x.Trials - } - return nil +// Deprecated: Use GetHPImportanceRequest.ProtoReflect.Descriptor instead. +func (*GetHPImportanceRequest) Descriptor() ([]byte, []int) { + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{45} } -func (x *ExpCompareTrialsSampleResponse) GetPromotedTrials() []int32 { +func (x *GetHPImportanceRequest) GetExperimentId() int32 { if x != nil { - return x.PromotedTrials + return x.ExperimentId } - return nil + return 0 } -func (x *ExpCompareTrialsSampleResponse) GetDemotedTrials() []int32 { +func (x *GetHPImportanceRequest) GetPeriodSeconds() int32 { if x != nil { - return x.DemotedTrials + return x.PeriodSeconds } - return nil + return 0 } -// Trigger the computation of hyperparameter importance on-demand for a specific -// metric on a specific experiment. -type ComputeHPImportanceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The id of the experiment. - ExperimentId int32 `protobuf:"varint,1,opt,name=experiment_id,json=experimentId,proto3" json:"experiment_id,omitempty"` - // A metric name. - MetricName string `protobuf:"bytes,2,opt,name=metric_name,json=metricName,proto3" json:"metric_name,omitempty"` - // The type of metric. - MetricType MetricType `protobuf:"varint,3,opt,name=metric_type,json=metricType,proto3,enum=determined.api.v1.MetricType" json:"metric_type,omitempty"` -} - -func (x *ComputeHPImportanceRequest) Reset() { - *x = ComputeHPImportanceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ComputeHPImportanceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ComputeHPImportanceRequest) ProtoMessage() {} - -func (x *ComputeHPImportanceRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[47] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ComputeHPImportanceRequest.ProtoReflect.Descriptor instead. -func (*ComputeHPImportanceRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{47} -} - -func (x *ComputeHPImportanceRequest) GetExperimentId() int32 { - if x != nil { - return x.ExperimentId - } - return 0 -} - -func (x *ComputeHPImportanceRequest) GetMetricName() string { - if x != nil { - return x.MetricName - } - return "" -} - -func (x *ComputeHPImportanceRequest) GetMetricType() MetricType { - if x != nil { - return x.MetricType - } - return MetricType_METRIC_TYPE_UNSPECIFIED -} - -// Response to ComputeHPImportanceRequest -type ComputeHPImportanceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ComputeHPImportanceResponse) Reset() { - *x = ComputeHPImportanceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[48] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ComputeHPImportanceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ComputeHPImportanceResponse) ProtoMessage() {} - -func (x *ComputeHPImportanceResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[48] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ComputeHPImportanceResponse.ProtoReflect.Descriptor instead. -func (*ComputeHPImportanceResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{48} -} - -// Retrieve the status and results of hyperparameter importance computation. -type GetHPImportanceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The id of the experiment. - ExperimentId int32 `protobuf:"varint,1,opt,name=experiment_id,json=experimentId,proto3" json:"experiment_id,omitempty"` - // Seconds to wait when polling for updates. - PeriodSeconds int32 `protobuf:"varint,2,opt,name=period_seconds,json=periodSeconds,proto3" json:"period_seconds,omitempty"` -} - -func (x *GetHPImportanceRequest) Reset() { - *x = GetHPImportanceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[49] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetHPImportanceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetHPImportanceRequest) ProtoMessage() {} - -func (x *GetHPImportanceRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[49] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetHPImportanceRequest.ProtoReflect.Descriptor instead. -func (*GetHPImportanceRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{49} -} - -func (x *GetHPImportanceRequest) GetExperimentId() int32 { - if x != nil { - return x.ExperimentId - } - return 0 -} - -func (x *GetHPImportanceRequest) GetPeriodSeconds() int32 { - if x != nil { - return x.PeriodSeconds - } - return 0 -} - -// Response to GetHPImportanceRequest -type GetHPImportanceResponse struct { +// Response to GetHPImportanceRequest +type GetHPImportanceResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -3297,7 +3002,7 @@ type GetHPImportanceResponse struct { func (x *GetHPImportanceResponse) Reset() { *x = GetHPImportanceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[50] + mi := &file_determined_api_v1_experiment_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3310,7 +3015,7 @@ func (x *GetHPImportanceResponse) String() string { func (*GetHPImportanceResponse) ProtoMessage() {} func (x *GetHPImportanceResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[50] + mi := &file_determined_api_v1_experiment_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3323,7 +3028,7 @@ func (x *GetHPImportanceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetHPImportanceResponse.ProtoReflect.Descriptor instead. func (*GetHPImportanceResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{50} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{46} } func (x *GetHPImportanceResponse) GetTrainingMetrics() map[string]*GetHPImportanceResponse_MetricHPImportance { @@ -3353,7 +3058,7 @@ type GetModelDefRequest struct { func (x *GetModelDefRequest) Reset() { *x = GetModelDefRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[51] + mi := &file_determined_api_v1_experiment_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3366,7 +3071,7 @@ func (x *GetModelDefRequest) String() string { func (*GetModelDefRequest) ProtoMessage() {} func (x *GetModelDefRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[51] + mi := &file_determined_api_v1_experiment_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3379,7 +3084,7 @@ func (x *GetModelDefRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetModelDefRequest.ProtoReflect.Descriptor instead. func (*GetModelDefRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{51} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{47} } func (x *GetModelDefRequest) GetExperimentId() int32 { @@ -3402,7 +3107,7 @@ type GetModelDefResponse struct { func (x *GetModelDefResponse) Reset() { *x = GetModelDefResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[52] + mi := &file_determined_api_v1_experiment_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3415,7 +3120,7 @@ func (x *GetModelDefResponse) String() string { func (*GetModelDefResponse) ProtoMessage() {} func (x *GetModelDefResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[52] + mi := &file_determined_api_v1_experiment_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3428,7 +3133,7 @@ func (x *GetModelDefResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetModelDefResponse.ProtoReflect.Descriptor instead. func (*GetModelDefResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{52} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{48} } func (x *GetModelDefResponse) GetB64Tgz() string { @@ -3453,7 +3158,7 @@ type MoveExperimentRequest struct { func (x *MoveExperimentRequest) Reset() { *x = MoveExperimentRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[53] + mi := &file_determined_api_v1_experiment_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3466,7 +3171,7 @@ func (x *MoveExperimentRequest) String() string { func (*MoveExperimentRequest) ProtoMessage() {} func (x *MoveExperimentRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[53] + mi := &file_determined_api_v1_experiment_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3479,7 +3184,7 @@ func (x *MoveExperimentRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveExperimentRequest.ProtoReflect.Descriptor instead. func (*MoveExperimentRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{53} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{49} } func (x *MoveExperimentRequest) GetExperimentId() int32 { @@ -3506,7 +3211,7 @@ type MoveExperimentResponse struct { func (x *MoveExperimentResponse) Reset() { *x = MoveExperimentResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[54] + mi := &file_determined_api_v1_experiment_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3519,7 +3224,7 @@ func (x *MoveExperimentResponse) String() string { func (*MoveExperimentResponse) ProtoMessage() {} func (x *MoveExperimentResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[54] + mi := &file_determined_api_v1_experiment_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3532,7 +3237,7 @@ func (x *MoveExperimentResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveExperimentResponse.ProtoReflect.Descriptor instead. func (*MoveExperimentResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{54} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{50} } // Request to get model definition file tree of an experiment. @@ -3548,7 +3253,7 @@ type GetModelDefTreeRequest struct { func (x *GetModelDefTreeRequest) Reset() { *x = GetModelDefTreeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[55] + mi := &file_determined_api_v1_experiment_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3561,7 +3266,7 @@ func (x *GetModelDefTreeRequest) String() string { func (*GetModelDefTreeRequest) ProtoMessage() {} func (x *GetModelDefTreeRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[55] + mi := &file_determined_api_v1_experiment_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3574,7 +3279,7 @@ func (x *GetModelDefTreeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetModelDefTreeRequest.ProtoReflect.Descriptor instead. func (*GetModelDefTreeRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{55} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{51} } func (x *GetModelDefTreeRequest) GetExperimentId() int32 { @@ -3597,7 +3302,7 @@ type GetModelDefTreeResponse struct { func (x *GetModelDefTreeResponse) Reset() { *x = GetModelDefTreeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[56] + mi := &file_determined_api_v1_experiment_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3610,7 +3315,7 @@ func (x *GetModelDefTreeResponse) String() string { func (*GetModelDefTreeResponse) ProtoMessage() {} func (x *GetModelDefTreeResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[56] + mi := &file_determined_api_v1_experiment_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3623,7 +3328,7 @@ func (x *GetModelDefTreeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetModelDefTreeResponse.ProtoReflect.Descriptor instead. func (*GetModelDefTreeResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{56} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{52} } func (x *GetModelDefTreeResponse) GetFiles() []*experimentv1.FileNode { @@ -3648,7 +3353,7 @@ type GetModelDefFileRequest struct { func (x *GetModelDefFileRequest) Reset() { *x = GetModelDefFileRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[57] + mi := &file_determined_api_v1_experiment_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3661,7 +3366,7 @@ func (x *GetModelDefFileRequest) String() string { func (*GetModelDefFileRequest) ProtoMessage() {} func (x *GetModelDefFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[57] + mi := &file_determined_api_v1_experiment_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3674,7 +3379,7 @@ func (x *GetModelDefFileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetModelDefFileRequest.ProtoReflect.Descriptor instead. func (*GetModelDefFileRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{57} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{53} } func (x *GetModelDefFileRequest) GetExperimentId() int32 { @@ -3704,7 +3409,7 @@ type GetModelDefFileResponse struct { func (x *GetModelDefFileResponse) Reset() { *x = GetModelDefFileResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[58] + mi := &file_determined_api_v1_experiment_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3717,7 +3422,7 @@ func (x *GetModelDefFileResponse) String() string { func (*GetModelDefFileResponse) ProtoMessage() {} func (x *GetModelDefFileResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[58] + mi := &file_determined_api_v1_experiment_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3730,7 +3435,7 @@ func (x *GetModelDefFileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetModelDefFileResponse.ProtoReflect.Descriptor instead. func (*GetModelDefFileResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{58} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{54} } func (x *GetModelDefFileResponse) GetFile() []byte { @@ -3753,7 +3458,7 @@ type GetSearcherEventsRequest struct { func (x *GetSearcherEventsRequest) Reset() { *x = GetSearcherEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[59] + mi := &file_determined_api_v1_experiment_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3766,7 +3471,7 @@ func (x *GetSearcherEventsRequest) String() string { func (*GetSearcherEventsRequest) ProtoMessage() {} func (x *GetSearcherEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[59] + mi := &file_determined_api_v1_experiment_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3779,7 +3484,7 @@ func (x *GetSearcherEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSearcherEventsRequest.ProtoReflect.Descriptor instead. func (*GetSearcherEventsRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{59} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{55} } func (x *GetSearcherEventsRequest) GetExperimentId() int32 { @@ -3802,7 +3507,7 @@ type GetSearcherEventsResponse struct { func (x *GetSearcherEventsResponse) Reset() { *x = GetSearcherEventsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[60] + mi := &file_determined_api_v1_experiment_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3815,7 +3520,7 @@ func (x *GetSearcherEventsResponse) String() string { func (*GetSearcherEventsResponse) ProtoMessage() {} func (x *GetSearcherEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[60] + mi := &file_determined_api_v1_experiment_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3828,7 +3533,7 @@ func (x *GetSearcherEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSearcherEventsResponse.ProtoReflect.Descriptor instead. func (*GetSearcherEventsResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{60} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{56} } func (x *GetSearcherEventsResponse) GetSearcherEvents() []*experimentv1.SearcherEvent { @@ -3855,7 +3560,7 @@ type PostSearcherOperationsRequest struct { func (x *PostSearcherOperationsRequest) Reset() { *x = PostSearcherOperationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[61] + mi := &file_determined_api_v1_experiment_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3868,7 +3573,7 @@ func (x *PostSearcherOperationsRequest) String() string { func (*PostSearcherOperationsRequest) ProtoMessage() {} func (x *PostSearcherOperationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[61] + mi := &file_determined_api_v1_experiment_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3881,7 +3586,7 @@ func (x *PostSearcherOperationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PostSearcherOperationsRequest.ProtoReflect.Descriptor instead. func (*PostSearcherOperationsRequest) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{61} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{57} } func (x *PostSearcherOperationsRequest) GetExperimentId() int32 { @@ -3915,7 +3620,7 @@ type PostSearcherOperationsResponse struct { func (x *PostSearcherOperationsResponse) Reset() { *x = PostSearcherOperationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[62] + mi := &file_determined_api_v1_experiment_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3928,7 +3633,7 @@ func (x *PostSearcherOperationsResponse) String() string { func (*PostSearcherOperationsResponse) ProtoMessage() {} func (x *PostSearcherOperationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[62] + mi := &file_determined_api_v1_experiment_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3941,7 +3646,7 @@ func (x *PostSearcherOperationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PostSearcherOperationsResponse.ProtoReflect.Descriptor instead. func (*PostSearcherOperationsResponse) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{62} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{58} } // Metric value and metadata for a trial that has progress this far. @@ -3963,7 +3668,7 @@ type TrialsSnapshotResponse_Trial struct { func (x *TrialsSnapshotResponse_Trial) Reset() { *x = TrialsSnapshotResponse_Trial{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[63] + mi := &file_determined_api_v1_experiment_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3976,7 +3681,7 @@ func (x *TrialsSnapshotResponse_Trial) String() string { func (*TrialsSnapshotResponse_Trial) ProtoMessage() {} func (x *TrialsSnapshotResponse_Trial) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[63] + mi := &file_determined_api_v1_experiment_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3989,7 +3694,7 @@ func (x *TrialsSnapshotResponse_Trial) ProtoReflect() protoreflect.Message { // Deprecated: Use TrialsSnapshotResponse_Trial.ProtoReflect.Descriptor instead. func (*TrialsSnapshotResponse_Trial) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{42, 0} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{40, 0} } func (x *TrialsSnapshotResponse_Trial) GetTrialId() int32 { @@ -4038,7 +3743,7 @@ type TrialsSampleResponse_Trial struct { func (x *TrialsSampleResponse_Trial) Reset() { *x = TrialsSampleResponse_Trial{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[64] + mi := &file_determined_api_v1_experiment_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4051,7 +3756,7 @@ func (x *TrialsSampleResponse_Trial) String() string { func (*TrialsSampleResponse_Trial) ProtoMessage() {} func (x *TrialsSampleResponse_Trial) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[64] + mi := &file_determined_api_v1_experiment_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4064,7 +3769,7 @@ func (x *TrialsSampleResponse_Trial) ProtoReflect() protoreflect.Message { // Deprecated: Use TrialsSampleResponse_Trial.ProtoReflect.Descriptor instead. func (*TrialsSampleResponse_Trial) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{44, 0} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{42, 0} } func (x *TrialsSampleResponse_Trial) GetTrialId() int32 { @@ -4088,82 +3793,6 @@ func (x *TrialsSampleResponse_Trial) GetData() []*DataPoint { return nil } -// Series of data points for an experiment trial. -type ExpCompareTrialsSampleResponse_ExpTrial struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The id of the trial. - TrialId int32 `protobuf:"varint,1,opt,name=trial_id,json=trialId,proto3" json:"trial_id,omitempty"` - // Hyperparamters values for this specific trial. - Hparams *_struct.Struct `protobuf:"bytes,2,opt,name=hparams,proto3" json:"hparams,omitempty"` - // Data related to a trial. - Data []*DataPoint `protobuf:"bytes,3,rep,name=data,proto3" json:"data,omitempty"` - // The id of the experiment for the trial - ExperimentId int32 `protobuf:"varint,4,opt,name=experiment_id,json=experimentId,proto3" json:"experiment_id,omitempty"` -} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) Reset() { - *x = ExpCompareTrialsSampleResponse_ExpTrial{} - if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[65] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExpCompareTrialsSampleResponse_ExpTrial) ProtoMessage() {} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[65] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExpCompareTrialsSampleResponse_ExpTrial.ProtoReflect.Descriptor instead. -func (*ExpCompareTrialsSampleResponse_ExpTrial) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{46, 0} -} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) GetTrialId() int32 { - if x != nil { - return x.TrialId - } - return 0 -} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) GetHparams() *_struct.Struct { - if x != nil { - return x.Hparams - } - return nil -} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) GetData() []*DataPoint { - if x != nil { - return x.Data - } - return nil -} - -func (x *ExpCompareTrialsSampleResponse_ExpTrial) GetExperimentId() int32 { - if x != nil { - return x.ExperimentId - } - return 0 -} - // Hyperparameter importance as computed with respect for one specific metric. type GetHPImportanceResponse_MetricHPImportance struct { state protoimpl.MessageState @@ -4187,7 +3816,7 @@ type GetHPImportanceResponse_MetricHPImportance struct { func (x *GetHPImportanceResponse_MetricHPImportance) Reset() { *x = GetHPImportanceResponse_MetricHPImportance{} if protoimpl.UnsafeEnabled { - mi := &file_determined_api_v1_experiment_proto_msgTypes[66] + mi := &file_determined_api_v1_experiment_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4200,7 +3829,7 @@ func (x *GetHPImportanceResponse_MetricHPImportance) String() string { func (*GetHPImportanceResponse_MetricHPImportance) ProtoMessage() {} func (x *GetHPImportanceResponse_MetricHPImportance) ProtoReflect() protoreflect.Message { - mi := &file_determined_api_v1_experiment_proto_msgTypes[66] + mi := &file_determined_api_v1_experiment_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4213,7 +3842,7 @@ func (x *GetHPImportanceResponse_MetricHPImportance) ProtoReflect() protoreflect // Deprecated: Use GetHPImportanceResponse_MetricHPImportance.ProtoReflect.Descriptor instead. func (*GetHPImportanceResponse_MetricHPImportance) Descriptor() ([]byte, []int) { - return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{50, 0} + return file_determined_api_v1_experiment_proto_rawDescGZIP(), []int{46, 0} } func (x *GetHPImportanceResponse_MetricHPImportance) GetHpImportance() map[string]float64 { @@ -4585,173 +4214,106 @@ var file_determined_api_v1_experiment_proto_rawDesc = []byte{ 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x70, 0x0a, - 0x1c, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, - 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, - 0x0e, 0x92, 0x41, 0x0b, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x52, - 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, - 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, - 0x79, 0x0a, 0x1d, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x69, - 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xfe, 0x01, 0x0a, 0x14, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x13, 0x92, 0x41, 0x10, 0xd2, - 0x01, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x52, - 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, - 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, - 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x31, 0x0a, 0x15, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x05, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xec, - 0x02, 0x0a, 0x15, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, - 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, - 0x13, 0x92, 0x41, 0x10, 0xd2, 0x01, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, - 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x62, 0x61, 0x74, - 0x63, 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x42, 0x17, 0x92, 0x41, 0x14, 0xd2, 0x01, 0x11, 0x62, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x52, 0x10, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, - 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, - 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xc7, 0x02, - 0x0a, 0x16, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x74, 0x72, 0x69, 0x61, - 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, - 0x61, 0x6c, 0x73, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, - 0x73, 0x1a, 0xd3, 0x01, 0x0a, 0x05, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x74, - 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, - 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x12, 0x2b, 0x0a, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x3a, 0x37, - 0x92, 0x41, 0x34, 0x0a, 0x32, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, - 0xd2, 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0xd2, 0x01, 0x06, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0xd2, 0x01, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x3a, 0x0e, 0x92, 0x41, 0x0b, 0x0a, 0x09, 0xd2, 0x01, - 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x89, 0x03, 0x0a, 0x13, 0x54, 0x72, 0x69, 0x61, - 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x38, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x13, 0x92, 0x41, 0x10, 0xd2, 0x01, 0x0d, 0x65, 0x78, - 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x52, 0x0c, 0x65, 0x78, 0x70, - 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x11, - 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, - 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, - 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, - 0x25, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, - 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x22, 0x8d, 0x03, 0x0a, 0x14, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, - 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x64, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x06, 0x74, 0x72, 0x69, - 0x61, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, - 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0e, 0x70, 0x72, - 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x54, 0x72, 0x69, - 0x61, 0x6c, 0x73, 0x1a, 0xaa, 0x01, 0x0a, 0x05, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x19, 0x0a, - 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x68, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x30, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, - 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x21, 0x92, - 0x41, 0x1e, 0x0a, 0x1c, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0xd2, - 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0xd2, 0x01, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x3a, 0x31, 0x92, 0x41, 0x2e, 0x0a, 0x2c, 0xd2, 0x01, 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, - 0xd2, 0x01, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, - 0x6c, 0x73, 0xd2, 0x01, 0x0e, 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, - 0x61, 0x6c, 0x73, 0x22, 0x96, 0x03, 0x0a, 0x1d, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x14, 0x92, - 0x41, 0x11, 0xd2, 0x01, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x73, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x73, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xfe, 0x01, + 0x0a, 0x14, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x13, 0x92, + 0x41, 0x10, 0xd2, 0x01, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x31, + 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x73, 0x22, 0xec, 0x02, 0x0a, 0x15, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x42, 0x13, 0x92, 0x41, 0x10, 0xd2, 0x01, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, + 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, + 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x11, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x42, 0x17, 0x92, 0x41, 0x14, 0xd2, 0x01, 0x11, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, + 0x52, 0x10, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x6d, 0x61, + 0x72, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x62, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x73, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x22, 0xc7, 0x02, 0x0a, 0x16, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x74, + 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, - 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, - 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6d, - 0x61, 0x78, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xdc, 0x03, 0x0a, - 0x1e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6c, + 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x06, 0x74, 0x72, + 0x69, 0x61, 0x6c, 0x73, 0x1a, 0xd3, 0x01, 0x0a, 0x05, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x19, + 0x0a, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x68, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x12, 0x2b, 0x0a, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x10, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x3a, 0x37, 0x92, 0x41, 0x34, 0x0a, 0x32, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0xd2, 0x01, 0x06, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0xd2, 0x01, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x3a, 0x0e, 0x92, 0x41, 0x0b, 0x0a, + 0x09, 0xd2, 0x01, 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x89, 0x03, 0x0a, 0x13, 0x54, + 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x13, 0x92, 0x41, 0x10, 0xd2, 0x01, + 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x52, 0x0c, + 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0b, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x54, 0x72, 0x69, 0x61, + 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x44, + 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, + 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x8d, 0x03, 0x0a, 0x14, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x52, 0x0a, 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x3a, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72, - 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x45, 0x78, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x06, 0x74, 0x72, 0x69, - 0x61, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, - 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0e, 0x70, 0x72, - 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x54, 0x72, 0x69, - 0x61, 0x6c, 0x73, 0x1a, 0xe2, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, + 0x45, 0x0a, 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x06, + 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, + 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x0e, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, + 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, + 0x54, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0xaa, 0x01, 0x0a, 0x05, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, @@ -4760,168 +4322,165 @@ var file_determined_api_v1_experiment_proto_rawDesc = []byte{ 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x3a, 0x31, 0x92, 0x41, 0x2e, 0x0a, 0x2c, 0xd2, 0x01, 0x08, 0x74, - 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0xd2, 0x01, 0x04, 0x64, 0x61, 0x74, 0x61, 0xd2, 0x01, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, - 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x3a, 0x31, 0x92, 0x41, 0x2e, 0x0a, 0x2c, 0xd2, - 0x01, 0x06, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0xd2, 0x01, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, - 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0xd2, 0x01, 0x0e, 0x64, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0xc8, 0x01, 0x0a, 0x1a, - 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, + 0x3a, 0x21, 0x92, 0x41, 0x1e, 0x0a, 0x1c, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0xd2, 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0xd2, 0x01, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x3a, 0x31, 0x92, 0x41, 0x2e, 0x0a, 0x2c, 0xd2, 0x01, 0x06, 0x74, 0x72, 0x69, + 0x61, 0x6c, 0x73, 0xd2, 0x01, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x72, 0x69, 0x61, 0x6c, 0x73, 0xd2, 0x01, 0x0e, 0x64, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, + 0x74, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0xc8, 0x01, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, + 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, + 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, + 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, + 0x65, 0x22, 0x1d, 0x0a, 0x1b, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x48, 0x50, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x64, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, - 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x42, 0x11, 0x92, 0x41, 0x0e, 0xd2, 0x01, 0x0b, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x22, 0x1d, 0x0a, 0x1b, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, - 0x65, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x80, 0x07, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x48, 0x50, + 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, + 0x67, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x74, + 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x70, + 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x1a, 0xcd, 0x02, 0x0a, 0x12, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x48, 0x50, 0x49, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x74, 0x0a, 0x0d, 0x68, 0x70, 0x5f, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4f, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x48, 0x70, + 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0c, 0x68, 0x70, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, + 0x13, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1f, + 0x0a, 0x0b, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x1a, + 0x3f, 0x0a, 0x11, 0x48, 0x70, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x81, 0x01, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x48, 0x50, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x83, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x3d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x2d, 0x92, 0x41, 0x2a, 0x0a, + 0x28, 0xd2, 0x01, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0xd2, 0x01, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x39, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, - 0x6e, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, - 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x80, 0x07, 0x0a, 0x17, - 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6e, - 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x3f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x72, - 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x12, 0x70, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x41, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0xcd, 0x02, 0x0a, 0x12, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x74, 0x0a, 0x0d, - 0x68, 0x70, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x4f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x2e, 0x48, 0x70, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x68, 0x70, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, - 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x12, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x1a, 0x3f, 0x0a, 0x11, 0x48, 0x70, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x81, 0x01, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x69, 0x6e, 0x69, - 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x3d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x83, 0x01, 0x0a, 0x16, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x50, 0x49, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x48, 0x50, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, - 0x2d, 0x92, 0x41, 0x2a, 0x0a, 0x28, 0xd2, 0x01, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, - 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xd2, 0x01, 0x12, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x39, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, - 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x17, 0x0a, 0x07, 0x62, 0x36, 0x34, 0x5f, 0x74, 0x67, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x62, 0x36, 0x34, 0x54, 0x67, 0x7a, 0x3a, 0x0f, 0x92, 0x41, 0x0c, 0x0a, 0x0a, - 0xd2, 0x01, 0x07, 0x62, 0x36, 0x34, 0x5f, 0x74, 0x67, 0x7a, 0x22, 0xa2, 0x01, 0x0a, 0x15, 0x4d, - 0x6f, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, - 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x3a, - 0x2e, 0x92, 0x41, 0x2b, 0x0a, 0x29, 0xd2, 0x01, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0xd2, - 0x01, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, - 0x18, 0x0a, 0x16, 0x4d, 0x6f, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x16, 0x47, 0x65, 0x74, - 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, - 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, - 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, - 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x51, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, - 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, - 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x22, 0x2d, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x46, - 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, - 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, - 0x3f, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, - 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, - 0x22, 0x6d, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, - 0x0f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x0e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0xf9, 0x01, 0x0a, 0x1d, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, - 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x5c, 0x0a, 0x13, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, - 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x12, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, 0x0a, 0x12, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, - 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, - 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x74, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x65, 0x64, 0x42, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x1e, 0x50, + 0x6e, 0x74, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x44, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x62, + 0x36, 0x34, 0x5f, 0x74, 0x67, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x36, + 0x34, 0x54, 0x67, 0x7a, 0x3a, 0x0f, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0xd2, 0x01, 0x07, 0x62, 0x36, + 0x34, 0x5f, 0x74, 0x67, 0x7a, 0x22, 0xa2, 0x01, 0x0a, 0x15, 0x4d, 0x6f, 0x76, 0x65, 0x45, 0x78, + 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, + 0x6e, 0x74, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x3a, 0x2e, 0x92, 0x41, 0x2b, 0x0a, + 0x29, 0xd2, 0x01, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0d, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x4d, 0x6f, + 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x44, 0x65, 0x66, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, + 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, + 0x65, 0x66, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, + 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x51, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x2d, 0x0a, 0x17, 0x47, + 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x3f, 0x0a, 0x18, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x6d, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x1d, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x5f, 0x0a, - 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, - 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x54, 0x52, - 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x49, 0x4e, 0x47, - 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x42, 0x35, - 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, - 0x61, 0x70, 0x69, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, + 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x5c, 0x0a, 0x13, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x5f, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x73, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x55, 0x0a, 0x12, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x42, + 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x1e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x5f, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1a, 0x0a, + 0x16, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x64, 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4937,7 +4496,7 @@ func file_determined_api_v1_experiment_proto_rawDescGZIP() []byte { } var file_determined_api_v1_experiment_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_determined_api_v1_experiment_proto_msgTypes = make([]protoimpl.MessageInfo, 70) +var file_determined_api_v1_experiment_proto_msgTypes = make([]protoimpl.MessageInfo, 65) var file_determined_api_v1_experiment_proto_goTypes = []interface{}{ (MetricType)(0), // 0: determined.api.v1.MetricType (GetExperimentsRequest_SortBy)(0), // 1: determined.api.v1.GetExperimentsRequest.SortBy @@ -4979,112 +4538,103 @@ var file_determined_api_v1_experiment_proto_goTypes = []interface{}{ (*CreateExperimentResponse)(nil), // 37: determined.api.v1.CreateExperimentResponse (*MetricNamesRequest)(nil), // 38: determined.api.v1.MetricNamesRequest (*MetricNamesResponse)(nil), // 39: determined.api.v1.MetricNamesResponse - (*ExpCompareMetricNamesRequest)(nil), // 40: determined.api.v1.ExpCompareMetricNamesRequest - (*ExpCompareMetricNamesResponse)(nil), // 41: determined.api.v1.ExpCompareMetricNamesResponse - (*MetricBatchesRequest)(nil), // 42: determined.api.v1.MetricBatchesRequest - (*MetricBatchesResponse)(nil), // 43: determined.api.v1.MetricBatchesResponse - (*TrialsSnapshotRequest)(nil), // 44: determined.api.v1.TrialsSnapshotRequest - (*TrialsSnapshotResponse)(nil), // 45: determined.api.v1.TrialsSnapshotResponse - (*TrialsSampleRequest)(nil), // 46: determined.api.v1.TrialsSampleRequest - (*TrialsSampleResponse)(nil), // 47: determined.api.v1.TrialsSampleResponse - (*ExpCompareTrialsSampleRequest)(nil), // 48: determined.api.v1.ExpCompareTrialsSampleRequest - (*ExpCompareTrialsSampleResponse)(nil), // 49: determined.api.v1.ExpCompareTrialsSampleResponse - (*ComputeHPImportanceRequest)(nil), // 50: determined.api.v1.ComputeHPImportanceRequest - (*ComputeHPImportanceResponse)(nil), // 51: determined.api.v1.ComputeHPImportanceResponse - (*GetHPImportanceRequest)(nil), // 52: determined.api.v1.GetHPImportanceRequest - (*GetHPImportanceResponse)(nil), // 53: determined.api.v1.GetHPImportanceResponse - (*GetModelDefRequest)(nil), // 54: determined.api.v1.GetModelDefRequest - (*GetModelDefResponse)(nil), // 55: determined.api.v1.GetModelDefResponse - (*MoveExperimentRequest)(nil), // 56: determined.api.v1.MoveExperimentRequest - (*MoveExperimentResponse)(nil), // 57: determined.api.v1.MoveExperimentResponse - (*GetModelDefTreeRequest)(nil), // 58: determined.api.v1.GetModelDefTreeRequest - (*GetModelDefTreeResponse)(nil), // 59: determined.api.v1.GetModelDefTreeResponse - (*GetModelDefFileRequest)(nil), // 60: determined.api.v1.GetModelDefFileRequest - (*GetModelDefFileResponse)(nil), // 61: determined.api.v1.GetModelDefFileResponse - (*GetSearcherEventsRequest)(nil), // 62: determined.api.v1.GetSearcherEventsRequest - (*GetSearcherEventsResponse)(nil), // 63: determined.api.v1.GetSearcherEventsResponse - (*PostSearcherOperationsRequest)(nil), // 64: determined.api.v1.PostSearcherOperationsRequest - (*PostSearcherOperationsResponse)(nil), // 65: determined.api.v1.PostSearcherOperationsResponse - (*TrialsSnapshotResponse_Trial)(nil), // 66: determined.api.v1.TrialsSnapshotResponse.Trial - (*TrialsSampleResponse_Trial)(nil), // 67: determined.api.v1.TrialsSampleResponse.Trial - (*ExpCompareTrialsSampleResponse_ExpTrial)(nil), // 68: determined.api.v1.ExpCompareTrialsSampleResponse.ExpTrial - (*GetHPImportanceResponse_MetricHPImportance)(nil), // 69: determined.api.v1.GetHPImportanceResponse.MetricHPImportance - nil, // 70: determined.api.v1.GetHPImportanceResponse.TrainingMetricsEntry - nil, // 71: determined.api.v1.GetHPImportanceResponse.ValidationMetricsEntry - nil, // 72: determined.api.v1.GetHPImportanceResponse.MetricHPImportance.HpImportanceEntry - (*timestamp.Timestamp)(nil), // 73: google.protobuf.Timestamp - (*experimentv1.Experiment)(nil), // 74: determined.experiment.v1.Experiment - (*jobv1.JobSummary)(nil), // 75: determined.job.v1.JobSummary - (OrderBy)(0), // 76: determined.api.v1.OrderBy - (*wrappers.BoolValue)(nil), // 77: google.protobuf.BoolValue - (experimentv1.State)(0), // 78: determined.experiment.v1.State - (*commonv1.Int32FieldFilter)(nil), // 79: determined.common.v1.Int32FieldFilter - (*Pagination)(nil), // 80: determined.api.v1.Pagination - (*_struct.Struct)(nil), // 81: google.protobuf.Struct - (*experimentv1.ExperimentSimulation)(nil), // 82: determined.experiment.v1.ExperimentSimulation - (*experimentv1.PatchExperiment)(nil), // 83: determined.experiment.v1.PatchExperiment - (checkpointv1.State)(0), // 84: determined.checkpoint.v1.State - (*checkpointv1.Checkpoint)(nil), // 85: determined.checkpoint.v1.Checkpoint - (*experimentv1.ValidationHistoryEntry)(nil), // 86: determined.experiment.v1.ValidationHistoryEntry - (*utilv1.File)(nil), // 87: determined.util.v1.File - (LaunchWarning)(0), // 88: determined.api.v1.LaunchWarning - (*experimentv1.FileNode)(nil), // 89: determined.experiment.v1.FileNode - (*experimentv1.SearcherEvent)(nil), // 90: determined.experiment.v1.SearcherEvent - (*experimentv1.SearcherOperation)(nil), // 91: determined.experiment.v1.SearcherOperation + (*MetricBatchesRequest)(nil), // 40: determined.api.v1.MetricBatchesRequest + (*MetricBatchesResponse)(nil), // 41: determined.api.v1.MetricBatchesResponse + (*TrialsSnapshotRequest)(nil), // 42: determined.api.v1.TrialsSnapshotRequest + (*TrialsSnapshotResponse)(nil), // 43: determined.api.v1.TrialsSnapshotResponse + (*TrialsSampleRequest)(nil), // 44: determined.api.v1.TrialsSampleRequest + (*TrialsSampleResponse)(nil), // 45: determined.api.v1.TrialsSampleResponse + (*ComputeHPImportanceRequest)(nil), // 46: determined.api.v1.ComputeHPImportanceRequest + (*ComputeHPImportanceResponse)(nil), // 47: determined.api.v1.ComputeHPImportanceResponse + (*GetHPImportanceRequest)(nil), // 48: determined.api.v1.GetHPImportanceRequest + (*GetHPImportanceResponse)(nil), // 49: determined.api.v1.GetHPImportanceResponse + (*GetModelDefRequest)(nil), // 50: determined.api.v1.GetModelDefRequest + (*GetModelDefResponse)(nil), // 51: determined.api.v1.GetModelDefResponse + (*MoveExperimentRequest)(nil), // 52: determined.api.v1.MoveExperimentRequest + (*MoveExperimentResponse)(nil), // 53: determined.api.v1.MoveExperimentResponse + (*GetModelDefTreeRequest)(nil), // 54: determined.api.v1.GetModelDefTreeRequest + (*GetModelDefTreeResponse)(nil), // 55: determined.api.v1.GetModelDefTreeResponse + (*GetModelDefFileRequest)(nil), // 56: determined.api.v1.GetModelDefFileRequest + (*GetModelDefFileResponse)(nil), // 57: determined.api.v1.GetModelDefFileResponse + (*GetSearcherEventsRequest)(nil), // 58: determined.api.v1.GetSearcherEventsRequest + (*GetSearcherEventsResponse)(nil), // 59: determined.api.v1.GetSearcherEventsResponse + (*PostSearcherOperationsRequest)(nil), // 60: determined.api.v1.PostSearcherOperationsRequest + (*PostSearcherOperationsResponse)(nil), // 61: determined.api.v1.PostSearcherOperationsResponse + (*TrialsSnapshotResponse_Trial)(nil), // 62: determined.api.v1.TrialsSnapshotResponse.Trial + (*TrialsSampleResponse_Trial)(nil), // 63: determined.api.v1.TrialsSampleResponse.Trial + (*GetHPImportanceResponse_MetricHPImportance)(nil), // 64: determined.api.v1.GetHPImportanceResponse.MetricHPImportance + nil, // 65: determined.api.v1.GetHPImportanceResponse.TrainingMetricsEntry + nil, // 66: determined.api.v1.GetHPImportanceResponse.ValidationMetricsEntry + nil, // 67: determined.api.v1.GetHPImportanceResponse.MetricHPImportance.HpImportanceEntry + (*timestamp.Timestamp)(nil), // 68: google.protobuf.Timestamp + (*experimentv1.Experiment)(nil), // 69: determined.experiment.v1.Experiment + (*jobv1.JobSummary)(nil), // 70: determined.job.v1.JobSummary + (OrderBy)(0), // 71: determined.api.v1.OrderBy + (*wrappers.BoolValue)(nil), // 72: google.protobuf.BoolValue + (experimentv1.State)(0), // 73: determined.experiment.v1.State + (*commonv1.Int32FieldFilter)(nil), // 74: determined.common.v1.Int32FieldFilter + (*Pagination)(nil), // 75: determined.api.v1.Pagination + (*_struct.Struct)(nil), // 76: google.protobuf.Struct + (*experimentv1.ExperimentSimulation)(nil), // 77: determined.experiment.v1.ExperimentSimulation + (*experimentv1.PatchExperiment)(nil), // 78: determined.experiment.v1.PatchExperiment + (checkpointv1.State)(0), // 79: determined.checkpoint.v1.State + (*checkpointv1.Checkpoint)(nil), // 80: determined.checkpoint.v1.Checkpoint + (*experimentv1.ValidationHistoryEntry)(nil), // 81: determined.experiment.v1.ValidationHistoryEntry + (*utilv1.File)(nil), // 82: determined.util.v1.File + (LaunchWarning)(0), // 83: determined.api.v1.LaunchWarning + (*experimentv1.FileNode)(nil), // 84: determined.experiment.v1.FileNode + (*experimentv1.SearcherEvent)(nil), // 85: determined.experiment.v1.SearcherEvent + (*experimentv1.SearcherOperation)(nil), // 86: determined.experiment.v1.SearcherOperation } var file_determined_api_v1_experiment_proto_depIdxs = []int32{ - 73, // 0: determined.api.v1.DataPointTime.time:type_name -> google.protobuf.Timestamp - 74, // 1: determined.api.v1.GetExperimentResponse.experiment:type_name -> determined.experiment.v1.Experiment - 75, // 2: determined.api.v1.GetExperimentResponse.job_summary:type_name -> determined.job.v1.JobSummary + 68, // 0: determined.api.v1.DataPointTime.time:type_name -> google.protobuf.Timestamp + 69, // 1: determined.api.v1.GetExperimentResponse.experiment:type_name -> determined.experiment.v1.Experiment + 70, // 2: determined.api.v1.GetExperimentResponse.job_summary:type_name -> determined.job.v1.JobSummary 1, // 3: determined.api.v1.GetExperimentsRequest.sort_by:type_name -> determined.api.v1.GetExperimentsRequest.SortBy - 76, // 4: determined.api.v1.GetExperimentsRequest.order_by:type_name -> determined.api.v1.OrderBy - 77, // 5: determined.api.v1.GetExperimentsRequest.archived:type_name -> google.protobuf.BoolValue - 78, // 6: determined.api.v1.GetExperimentsRequest.states:type_name -> determined.experiment.v1.State - 79, // 7: determined.api.v1.GetExperimentsRequest.experiment_id_filter:type_name -> determined.common.v1.Int32FieldFilter - 74, // 8: determined.api.v1.GetExperimentsResponse.experiments:type_name -> determined.experiment.v1.Experiment - 80, // 9: determined.api.v1.GetExperimentsResponse.pagination:type_name -> determined.api.v1.Pagination - 81, // 10: determined.api.v1.PreviewHPSearchRequest.config:type_name -> google.protobuf.Struct - 82, // 11: determined.api.v1.PreviewHPSearchResponse.simulation:type_name -> determined.experiment.v1.ExperimentSimulation - 83, // 12: determined.api.v1.PatchExperimentRequest.experiment:type_name -> determined.experiment.v1.PatchExperiment - 74, // 13: determined.api.v1.PatchExperimentResponse.experiment:type_name -> determined.experiment.v1.Experiment + 71, // 4: determined.api.v1.GetExperimentsRequest.order_by:type_name -> determined.api.v1.OrderBy + 72, // 5: determined.api.v1.GetExperimentsRequest.archived:type_name -> google.protobuf.BoolValue + 73, // 6: determined.api.v1.GetExperimentsRequest.states:type_name -> determined.experiment.v1.State + 74, // 7: determined.api.v1.GetExperimentsRequest.experiment_id_filter:type_name -> determined.common.v1.Int32FieldFilter + 69, // 8: determined.api.v1.GetExperimentsResponse.experiments:type_name -> determined.experiment.v1.Experiment + 75, // 9: determined.api.v1.GetExperimentsResponse.pagination:type_name -> determined.api.v1.Pagination + 76, // 10: determined.api.v1.PreviewHPSearchRequest.config:type_name -> google.protobuf.Struct + 77, // 11: determined.api.v1.PreviewHPSearchResponse.simulation:type_name -> determined.experiment.v1.ExperimentSimulation + 78, // 12: determined.api.v1.PatchExperimentRequest.experiment:type_name -> determined.experiment.v1.PatchExperiment + 69, // 13: determined.api.v1.PatchExperimentResponse.experiment:type_name -> determined.experiment.v1.Experiment 2, // 14: determined.api.v1.GetExperimentCheckpointsRequest.sort_by:type_name -> determined.api.v1.GetExperimentCheckpointsRequest.SortBy - 76, // 15: determined.api.v1.GetExperimentCheckpointsRequest.order_by:type_name -> determined.api.v1.OrderBy - 84, // 16: determined.api.v1.GetExperimentCheckpointsRequest.states:type_name -> determined.checkpoint.v1.State - 85, // 17: determined.api.v1.GetExperimentCheckpointsResponse.checkpoints:type_name -> determined.checkpoint.v1.Checkpoint - 80, // 18: determined.api.v1.GetExperimentCheckpointsResponse.pagination:type_name -> determined.api.v1.Pagination - 86, // 19: determined.api.v1.GetExperimentValidationHistoryResponse.validation_history:type_name -> determined.experiment.v1.ValidationHistoryEntry - 87, // 20: determined.api.v1.CreateExperimentRequest.model_definition:type_name -> determined.util.v1.File - 73, // 21: determined.api.v1.CreateExperimentRequest.git_commit_date:type_name -> google.protobuf.Timestamp - 74, // 22: determined.api.v1.CreateExperimentResponse.experiment:type_name -> determined.experiment.v1.Experiment - 81, // 23: determined.api.v1.CreateExperimentResponse.config:type_name -> google.protobuf.Struct - 88, // 24: determined.api.v1.CreateExperimentResponse.warnings:type_name -> determined.api.v1.LaunchWarning + 71, // 15: determined.api.v1.GetExperimentCheckpointsRequest.order_by:type_name -> determined.api.v1.OrderBy + 79, // 16: determined.api.v1.GetExperimentCheckpointsRequest.states:type_name -> determined.checkpoint.v1.State + 80, // 17: determined.api.v1.GetExperimentCheckpointsResponse.checkpoints:type_name -> determined.checkpoint.v1.Checkpoint + 75, // 18: determined.api.v1.GetExperimentCheckpointsResponse.pagination:type_name -> determined.api.v1.Pagination + 81, // 19: determined.api.v1.GetExperimentValidationHistoryResponse.validation_history:type_name -> determined.experiment.v1.ValidationHistoryEntry + 82, // 20: determined.api.v1.CreateExperimentRequest.model_definition:type_name -> determined.util.v1.File + 68, // 21: determined.api.v1.CreateExperimentRequest.git_commit_date:type_name -> google.protobuf.Timestamp + 69, // 22: determined.api.v1.CreateExperimentResponse.experiment:type_name -> determined.experiment.v1.Experiment + 76, // 23: determined.api.v1.CreateExperimentResponse.config:type_name -> google.protobuf.Struct + 83, // 24: determined.api.v1.CreateExperimentResponse.warnings:type_name -> determined.api.v1.LaunchWarning 0, // 25: determined.api.v1.MetricBatchesRequest.metric_type:type_name -> determined.api.v1.MetricType 0, // 26: determined.api.v1.TrialsSnapshotRequest.metric_type:type_name -> determined.api.v1.MetricType - 66, // 27: determined.api.v1.TrialsSnapshotResponse.trials:type_name -> determined.api.v1.TrialsSnapshotResponse.Trial + 62, // 27: determined.api.v1.TrialsSnapshotResponse.trials:type_name -> determined.api.v1.TrialsSnapshotResponse.Trial 0, // 28: determined.api.v1.TrialsSampleRequest.metric_type:type_name -> determined.api.v1.MetricType - 67, // 29: determined.api.v1.TrialsSampleResponse.trials:type_name -> determined.api.v1.TrialsSampleResponse.Trial - 0, // 30: determined.api.v1.ExpCompareTrialsSampleRequest.metric_type:type_name -> determined.api.v1.MetricType - 68, // 31: determined.api.v1.ExpCompareTrialsSampleResponse.trials:type_name -> determined.api.v1.ExpCompareTrialsSampleResponse.ExpTrial - 0, // 32: determined.api.v1.ComputeHPImportanceRequest.metric_type:type_name -> determined.api.v1.MetricType - 70, // 33: determined.api.v1.GetHPImportanceResponse.training_metrics:type_name -> determined.api.v1.GetHPImportanceResponse.TrainingMetricsEntry - 71, // 34: determined.api.v1.GetHPImportanceResponse.validation_metrics:type_name -> determined.api.v1.GetHPImportanceResponse.ValidationMetricsEntry - 89, // 35: determined.api.v1.GetModelDefTreeResponse.files:type_name -> determined.experiment.v1.FileNode - 90, // 36: determined.api.v1.GetSearcherEventsResponse.searcher_events:type_name -> determined.experiment.v1.SearcherEvent - 91, // 37: determined.api.v1.PostSearcherOperationsRequest.searcher_operations:type_name -> determined.experiment.v1.SearcherOperation - 90, // 38: determined.api.v1.PostSearcherOperationsRequest.triggered_by_event:type_name -> determined.experiment.v1.SearcherEvent - 81, // 39: determined.api.v1.TrialsSnapshotResponse.Trial.hparams:type_name -> google.protobuf.Struct - 81, // 40: determined.api.v1.TrialsSampleResponse.Trial.hparams:type_name -> google.protobuf.Struct - 3, // 41: determined.api.v1.TrialsSampleResponse.Trial.data:type_name -> determined.api.v1.DataPoint - 81, // 42: determined.api.v1.ExpCompareTrialsSampleResponse.ExpTrial.hparams:type_name -> google.protobuf.Struct - 3, // 43: determined.api.v1.ExpCompareTrialsSampleResponse.ExpTrial.data:type_name -> determined.api.v1.DataPoint - 72, // 44: determined.api.v1.GetHPImportanceResponse.MetricHPImportance.hp_importance:type_name -> determined.api.v1.GetHPImportanceResponse.MetricHPImportance.HpImportanceEntry - 69, // 45: determined.api.v1.GetHPImportanceResponse.TrainingMetricsEntry.value:type_name -> determined.api.v1.GetHPImportanceResponse.MetricHPImportance - 69, // 46: determined.api.v1.GetHPImportanceResponse.ValidationMetricsEntry.value:type_name -> determined.api.v1.GetHPImportanceResponse.MetricHPImportance - 47, // [47:47] is the sub-list for method output_type - 47, // [47:47] is the sub-list for method input_type - 47, // [47:47] is the sub-list for extension type_name - 47, // [47:47] is the sub-list for extension extendee - 0, // [0:47] is the sub-list for field type_name + 63, // 29: determined.api.v1.TrialsSampleResponse.trials:type_name -> determined.api.v1.TrialsSampleResponse.Trial + 0, // 30: determined.api.v1.ComputeHPImportanceRequest.metric_type:type_name -> determined.api.v1.MetricType + 65, // 31: determined.api.v1.GetHPImportanceResponse.training_metrics:type_name -> determined.api.v1.GetHPImportanceResponse.TrainingMetricsEntry + 66, // 32: determined.api.v1.GetHPImportanceResponse.validation_metrics:type_name -> determined.api.v1.GetHPImportanceResponse.ValidationMetricsEntry + 84, // 33: determined.api.v1.GetModelDefTreeResponse.files:type_name -> determined.experiment.v1.FileNode + 85, // 34: determined.api.v1.GetSearcherEventsResponse.searcher_events:type_name -> determined.experiment.v1.SearcherEvent + 86, // 35: determined.api.v1.PostSearcherOperationsRequest.searcher_operations:type_name -> determined.experiment.v1.SearcherOperation + 85, // 36: determined.api.v1.PostSearcherOperationsRequest.triggered_by_event:type_name -> determined.experiment.v1.SearcherEvent + 76, // 37: determined.api.v1.TrialsSnapshotResponse.Trial.hparams:type_name -> google.protobuf.Struct + 76, // 38: determined.api.v1.TrialsSampleResponse.Trial.hparams:type_name -> google.protobuf.Struct + 3, // 39: determined.api.v1.TrialsSampleResponse.Trial.data:type_name -> determined.api.v1.DataPoint + 67, // 40: determined.api.v1.GetHPImportanceResponse.MetricHPImportance.hp_importance:type_name -> determined.api.v1.GetHPImportanceResponse.MetricHPImportance.HpImportanceEntry + 64, // 41: determined.api.v1.GetHPImportanceResponse.TrainingMetricsEntry.value:type_name -> determined.api.v1.GetHPImportanceResponse.MetricHPImportance + 64, // 42: determined.api.v1.GetHPImportanceResponse.ValidationMetricsEntry.value:type_name -> determined.api.v1.GetHPImportanceResponse.MetricHPImportance + 43, // [43:43] is the sub-list for method output_type + 43, // [43:43] is the sub-list for method input_type + 43, // [43:43] is the sub-list for extension type_name + 43, // [43:43] is the sub-list for extension extendee + 0, // [0:43] is the sub-list for field type_name } func init() { file_determined_api_v1_experiment_proto_init() } @@ -5540,30 +5090,6 @@ func file_determined_api_v1_experiment_proto_init() { } } file_determined_api_v1_experiment_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExpCompareMetricNamesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_determined_api_v1_experiment_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExpCompareMetricNamesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_determined_api_v1_experiment_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MetricBatchesRequest); i { case 0: return &v.state @@ -5575,7 +5101,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MetricBatchesResponse); i { case 0: return &v.state @@ -5587,7 +5113,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrialsSnapshotRequest); i { case 0: return &v.state @@ -5599,7 +5125,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrialsSnapshotResponse); i { case 0: return &v.state @@ -5611,7 +5137,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrialsSampleRequest); i { case 0: return &v.state @@ -5623,7 +5149,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrialsSampleResponse); i { case 0: return &v.state @@ -5635,31 +5161,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExpCompareTrialsSampleRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_determined_api_v1_experiment_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExpCompareTrialsSampleResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_determined_api_v1_experiment_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ComputeHPImportanceRequest); i { case 0: return &v.state @@ -5671,7 +5173,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ComputeHPImportanceResponse); i { case 0: return &v.state @@ -5683,7 +5185,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetHPImportanceRequest); i { case 0: return &v.state @@ -5695,7 +5197,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetHPImportanceResponse); i { case 0: return &v.state @@ -5707,7 +5209,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetModelDefRequest); i { case 0: return &v.state @@ -5719,7 +5221,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetModelDefResponse); i { case 0: return &v.state @@ -5731,7 +5233,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MoveExperimentRequest); i { case 0: return &v.state @@ -5743,7 +5245,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MoveExperimentResponse); i { case 0: return &v.state @@ -5755,7 +5257,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetModelDefTreeRequest); i { case 0: return &v.state @@ -5767,7 +5269,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetModelDefTreeResponse); i { case 0: return &v.state @@ -5779,7 +5281,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetModelDefFileRequest); i { case 0: return &v.state @@ -5791,7 +5293,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetModelDefFileResponse); i { case 0: return &v.state @@ -5803,7 +5305,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSearcherEventsRequest); i { case 0: return &v.state @@ -5815,7 +5317,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSearcherEventsResponse); i { case 0: return &v.state @@ -5827,7 +5329,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PostSearcherOperationsRequest); i { case 0: return &v.state @@ -5839,7 +5341,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PostSearcherOperationsResponse); i { case 0: return &v.state @@ -5851,7 +5353,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrialsSnapshotResponse_Trial); i { case 0: return &v.state @@ -5863,7 +5365,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrialsSampleResponse_Trial); i { case 0: return &v.state @@ -5875,19 +5377,7 @@ func file_determined_api_v1_experiment_proto_init() { return nil } } - file_determined_api_v1_experiment_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExpCompareTrialsSampleResponse_ExpTrial); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_determined_api_v1_experiment_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + file_determined_api_v1_experiment_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetHPImportanceResponse_MetricHPImportance); i { case 0: return &v.state @@ -5907,7 +5397,7 @@ func file_determined_api_v1_experiment_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_determined_api_v1_experiment_proto_rawDesc, NumEnums: 3, - NumMessages: 70, + NumMessages: 65, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/pkg/trialv1/trial.pb.go b/proto/pkg/trialv1/trial.pb.go index b3e5746615f..ea53b43e59a 100644 --- a/proto/pkg/trialv1/trial.pb.go +++ b/proto/pkg/trialv1/trial.pb.go @@ -320,8 +320,6 @@ type MetricsWorkload struct { // The time the workload finished or was stopped. EndTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - // The current validation state. - State experimentv1.State `protobuf:"varint,3,opt,name=state,proto3,enum=determined.experiment.v1.State" json:"state,omitempty"` // Metrics. Metrics *commonv1.Metrics `protobuf:"bytes,40,opt,name=metrics,proto3" json:"metrics,omitempty"` // Number of inputs processed. @@ -371,13 +369,6 @@ func (x *MetricsWorkload) GetEndTime() *timestamp.Timestamp { return nil } -func (x *MetricsWorkload) GetState() experimentv1.State { - if x != nil { - return x.State - } - return experimentv1.State_STATE_UNSPECIFIED -} - func (x *MetricsWorkload) GetMetrics() *commonv1.Metrics { if x != nil { return x.Metrics @@ -1060,192 +1051,189 @@ var file_determined_trial_v1_trial_proto_rawDesc = []byte{ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x1d, 0x92, 0x41, 0x1a, 0x0a, 0x18, 0xd2, 0x01, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x73, 0x22, 0xb6, 0x02, 0x0a, 0x0f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x57, 0x6f, + 0x65, 0x73, 0x22, 0xff, 0x01, 0x0a, 0x0f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x64, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, - 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x21, 0x0a, - 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x05, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x73, 0x3a, 0x34, 0x92, 0x41, 0x31, 0x0a, 0x2f, 0xd2, 0x01, 0x07, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xd2, 0x01, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0xd2, 0x01, - 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0xd2, 0x01, 0x0d, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x8b, 0x08, 0x0a, 0x05, - 0x54, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, - 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x37, 0x0a, + 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, + 0x6e, 0x75, 0x6d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x3a, 0x34, + 0x92, 0x41, 0x31, 0x0a, 0x2f, 0xd2, 0x01, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xd2, + 0x01, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0xd2, 0x01, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x65, 0x73, 0x22, 0x8b, 0x08, 0x0a, 0x05, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, + 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x64, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x12, - 0x31, 0x0a, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x0f, 0x62, 0x65, - 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0e, 0x62, 0x65, 0x73, 0x74, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x11, 0x6c, 0x61, 0x74, - 0x65, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, - 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x10, 0x6c, 0x61, 0x74, 0x65, - 0x73, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x50, 0x0a, 0x0f, - 0x62, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0e, - 0x62, 0x65, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4d, - 0x0a, 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, - 0x67, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0e, 0x6c, - 0x61, 0x74, 0x65, 0x73, 0x74, 0x54, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, - 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x43, - 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x77, 0x61, 0x72, 0x6d, - 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x77, 0x61, - 0x72, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x32, - 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, - 0x7a, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x5a, 0x92, - 0x41, 0x57, 0x0a, 0x55, 0xd2, 0x01, 0x02, 0x69, 0x64, 0xd2, 0x01, 0x0c, 0x65, 0x78, 0x70, 0x65, - 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0xd2, 0x01, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0xd2, 0x01, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x08, 0x72, - 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0xd2, 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0xd2, 0x01, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x22, 0x9b, 0x03, 0x0a, 0x19, 0x54, 0x72, - 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x70, 0x75, 0x55, 0x75, 0x69, 0x64, 0x12, 0x62, 0x0a, 0x0b, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x41, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, - 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x9b, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x46, 0x49, - 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, 0x0a, - 0x1b, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x10, 0x01, 0x12, 0x1f, - 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, - 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, - 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, - 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x43, 0x10, 0x03, 0x3a, 0x17, - 0x92, 0x41, 0x14, 0x0a, 0x12, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, - 0xd2, 0x01, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x81, 0x02, 0x0a, 0x19, 0x54, 0x72, 0x69, 0x61, - 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x02, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x52, 0x07, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x73, 0x12, 0x46, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, - 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x3a, 0x2e, 0x92, 0x41, 0x2b, - 0x0a, 0x29, 0xd2, 0x01, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0xd2, 0x01, 0x07, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x73, 0xd2, 0x01, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x73, 0xd2, 0x01, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x22, 0xda, 0x01, 0x0a, 0x0e, - 0x54, 0x72, 0x69, 0x61, 0x6c, 0x45, 0x61, 0x72, 0x6c, 0x79, 0x45, 0x78, 0x69, 0x74, 0x12, 0x48, - 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, + 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x68, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x0f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x0e, 0x62, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x11, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x10, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x50, 0x0a, 0x0f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x45, 0x61, 0x72, 0x6c, 0x79, 0x45, - 0x78, 0x69, 0x74, 0x2e, 0x45, 0x78, 0x69, 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x6e, 0x0a, 0x0c, 0x45, 0x78, 0x69, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x49, 0x54, - 0x45, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x58, 0x49, 0x54, 0x45, - 0x44, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, - 0x5f, 0x48, 0x50, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x5f, 0x48, 0x50, 0x10, 0x03, 0x3a, 0x0e, 0x92, 0x41, 0x0b, 0x0a, 0x09, 0xd2, - 0x01, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xea, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x69, - 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x69, - 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x72, 0x69, - 0x61, 0x6c, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x75, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x72, 0x69, 0x61, - 0x6c, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x65, 0x70, 0x73, 0x5f, - 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0e, 0x73, 0x74, 0x65, 0x70, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, - 0x37, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x3a, 0x3b, 0x92, 0x41, 0x38, 0x0a, 0x36, 0xd2, - 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0c, 0x74, 0x72, 0x69, - 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0f, 0x73, 0x74, 0x65, 0x70, - 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0xd2, 0x01, 0x07, 0x6d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x7a, 0x0a, 0x0e, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, - 0x6f, 0x75, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x6c, 0x6f, - 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x05, 0x52, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x3a, - 0x20, 0x92, 0x41, 0x1d, 0x0a, 0x1b, 0xd2, 0x01, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0xd2, 0x01, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0xd2, 0x01, 0x05, 0x73, 0x6c, 0x6f, 0x74, - 0x73, 0x22, 0x3a, 0x0a, 0x13, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x0d, - 0x92, 0x41, 0x0a, 0x0a, 0x08, 0xd2, 0x01, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, 0xec, 0x01, - 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, - 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, - 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, - 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, - 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, - 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, - 0x47, 0x5f, 0x4b, 0x49, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x54, - 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x4f, 0x4d, - 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x54, 0x41, 0x54, - 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, - 0x45, 0x4c, 0x45, 0x44, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, - 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x53, - 0x54, 0x41, 0x54, 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x09, 0x42, 0x37, 0x5a, 0x35, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, - 0x69, 0x61, 0x6c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0e, 0x62, 0x65, 0x73, 0x74, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4d, 0x0a, 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, + 0x74, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, + 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x54, 0x72, + 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, + 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x61, 0x6c, + 0x6c, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x77, 0x61, 0x72, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x77, 0x61, 0x72, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x5a, 0x92, 0x41, 0x57, 0x0a, 0x55, 0xd2, 0x01, 0x02, + 0x69, 0x64, 0xd2, 0x01, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0xd2, 0x01, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0xd2, 0x01, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x08, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, + 0xd2, 0x01, 0x07, 0x68, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0xd2, 0x01, 0x15, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x65, 0x64, 0x22, 0x9b, 0x03, 0x0a, 0x19, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x70, + 0x75, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x70, + 0x75, 0x55, 0x75, 0x69, 0x64, 0x12, 0x62, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x41, 0x2e, 0x64, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x12, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, + 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, + 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, + 0x59, 0x53, 0x54, 0x45, 0x4d, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x46, 0x49, + 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x54, 0x49, 0x4d, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x46, + 0x49, 0x4c, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x4d, 0x49, 0x53, 0x43, 0x10, 0x03, 0x3a, 0x17, 0x92, 0x41, 0x14, 0x0a, 0x12, 0xd2, 0x01, + 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x81, 0x02, 0x0a, 0x19, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, + 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x02, 0x52, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x12, 0x3a, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x12, 0x46, 0x0a, 0x06, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x3a, 0x2e, 0x92, 0x41, 0x2b, 0x0a, 0x29, 0xd2, 0x01, 0x06, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0xd2, 0x01, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0xd2, 0x01, + 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0xd2, 0x01, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x22, 0xda, 0x01, 0x0a, 0x0e, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x45, 0x61, + 0x72, 0x6c, 0x79, 0x45, 0x78, 0x69, 0x74, 0x12, 0x48, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, + 0x69, 0x61, 0x6c, 0x45, 0x61, 0x72, 0x6c, 0x79, 0x45, 0x78, 0x69, 0x74, 0x2e, 0x45, 0x78, 0x69, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x22, 0x6e, 0x0a, 0x0c, 0x45, 0x78, 0x69, 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, + 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x48, 0x50, 0x10, 0x01, 0x12, 0x21, + 0x0a, 0x1d, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x48, 0x50, 0x10, + 0x03, 0x3a, 0x0e, 0x92, 0x41, 0x0b, 0x0a, 0x09, 0xd2, 0x01, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x22, 0xea, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x20, 0x0a, + 0x0c, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, + 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x65, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x73, 0x74, 0x65, 0x70, 0x73, 0x43, + 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x3a, 0x3b, 0x92, 0x41, 0x38, 0x0a, 0x36, 0xd2, 0x01, 0x08, 0x74, 0x72, 0x69, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0c, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x69, 0x64, 0xd2, 0x01, 0x0f, 0x73, 0x74, 0x65, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0xd2, 0x01, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x7a, + 0x0a, 0x0e, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x72, 0x61, + 0x6e, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x05, 0x52, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x3a, 0x20, 0x92, 0x41, 0x1d, 0x0a, 0x1b, 0xd2, + 0x01, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0xd2, 0x01, 0x04, 0x72, 0x61, + 0x6e, 0x6b, 0xd2, 0x01, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x22, 0x3a, 0x0a, 0x13, 0x54, 0x72, + 0x69, 0x61, 0x6c, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x0d, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0xd2, 0x01, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, 0xec, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x45, + 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, + 0x54, 0x45, 0x5f, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x41, + 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x4b, 0x49, 0x4c, 0x4c, 0x45, + 0x44, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, + 0x50, 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, + 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, + 0x49, 0x4e, 0x47, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x07, 0x12, + 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, + 0x45, 0x44, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x45, 0x52, + 0x52, 0x4f, 0x52, 0x10, 0x09, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2d, 0x61, + 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x76, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1279,8 +1267,8 @@ var file_determined_trial_v1_trial_proto_goTypes = []interface{}{ (*timestamp.Timestamp)(nil), // 13: google.protobuf.Timestamp (checkpointv1.State)(0), // 14: determined.checkpoint.v1.State (*_struct.Struct)(nil), // 15: google.protobuf.Struct - (experimentv1.State)(0), // 16: determined.experiment.v1.State - (*commonv1.Metrics)(nil), // 17: determined.common.v1.Metrics + (*commonv1.Metrics)(nil), // 16: determined.common.v1.Metrics + (experimentv1.State)(0), // 17: determined.experiment.v1.State } var file_determined_trial_v1_trial_proto_depIdxs = []int32{ 13, // 0: determined.trial.v1.CheckpointWorkload.end_time:type_name -> google.protobuf.Timestamp @@ -1288,26 +1276,25 @@ var file_determined_trial_v1_trial_proto_depIdxs = []int32{ 12, // 2: determined.trial.v1.CheckpointWorkload.resources:type_name -> determined.trial.v1.CheckpointWorkload.ResourcesEntry 15, // 3: determined.trial.v1.CheckpointWorkload.metadata:type_name -> google.protobuf.Struct 13, // 4: determined.trial.v1.MetricsWorkload.end_time:type_name -> google.protobuf.Timestamp - 16, // 5: determined.trial.v1.MetricsWorkload.state:type_name -> determined.experiment.v1.State - 17, // 6: determined.trial.v1.MetricsWorkload.metrics:type_name -> determined.common.v1.Metrics - 13, // 7: determined.trial.v1.Trial.start_time:type_name -> google.protobuf.Timestamp - 13, // 8: determined.trial.v1.Trial.end_time:type_name -> google.protobuf.Timestamp - 16, // 9: determined.trial.v1.Trial.state:type_name -> determined.experiment.v1.State - 15, // 10: determined.trial.v1.Trial.hparams:type_name -> google.protobuf.Struct - 4, // 11: determined.trial.v1.Trial.best_validation:type_name -> determined.trial.v1.MetricsWorkload - 4, // 12: determined.trial.v1.Trial.latest_validation:type_name -> determined.trial.v1.MetricsWorkload - 3, // 13: determined.trial.v1.Trial.best_checkpoint:type_name -> determined.trial.v1.CheckpointWorkload - 4, // 14: determined.trial.v1.Trial.latest_training:type_name -> determined.trial.v1.MetricsWorkload - 1, // 15: determined.trial.v1.TrialProfilerMetricLabels.metric_type:type_name -> determined.trial.v1.TrialProfilerMetricLabels.ProfilerMetricType - 13, // 16: determined.trial.v1.TrialProfilerMetricsBatch.timestamps:type_name -> google.protobuf.Timestamp - 6, // 17: determined.trial.v1.TrialProfilerMetricsBatch.labels:type_name -> determined.trial.v1.TrialProfilerMetricLabels - 2, // 18: determined.trial.v1.TrialEarlyExit.reason:type_name -> determined.trial.v1.TrialEarlyExit.ExitedReason - 17, // 19: determined.trial.v1.TrialMetrics.metrics:type_name -> determined.common.v1.Metrics - 20, // [20:20] is the sub-list for method output_type - 20, // [20:20] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 16, // 5: determined.trial.v1.MetricsWorkload.metrics:type_name -> determined.common.v1.Metrics + 13, // 6: determined.trial.v1.Trial.start_time:type_name -> google.protobuf.Timestamp + 13, // 7: determined.trial.v1.Trial.end_time:type_name -> google.protobuf.Timestamp + 17, // 8: determined.trial.v1.Trial.state:type_name -> determined.experiment.v1.State + 15, // 9: determined.trial.v1.Trial.hparams:type_name -> google.protobuf.Struct + 4, // 10: determined.trial.v1.Trial.best_validation:type_name -> determined.trial.v1.MetricsWorkload + 4, // 11: determined.trial.v1.Trial.latest_validation:type_name -> determined.trial.v1.MetricsWorkload + 3, // 12: determined.trial.v1.Trial.best_checkpoint:type_name -> determined.trial.v1.CheckpointWorkload + 4, // 13: determined.trial.v1.Trial.latest_training:type_name -> determined.trial.v1.MetricsWorkload + 1, // 14: determined.trial.v1.TrialProfilerMetricLabels.metric_type:type_name -> determined.trial.v1.TrialProfilerMetricLabels.ProfilerMetricType + 13, // 15: determined.trial.v1.TrialProfilerMetricsBatch.timestamps:type_name -> google.protobuf.Timestamp + 6, // 16: determined.trial.v1.TrialProfilerMetricsBatch.labels:type_name -> determined.trial.v1.TrialProfilerMetricLabels + 2, // 17: determined.trial.v1.TrialEarlyExit.reason:type_name -> determined.trial.v1.TrialEarlyExit.ExitedReason + 16, // 18: determined.trial.v1.TrialMetrics.metrics:type_name -> determined.common.v1.Metrics + 19, // [19:19] is the sub-list for method output_type + 19, // [19:19] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_determined_trial_v1_trial_proto_init() } diff --git a/proto/src/determined/api/v1/api.proto b/proto/src/determined/api/v1/api.proto index 44d226d0211..ae55b7fd45e 100644 --- a/proto/src/determined/api/v1/api.proto +++ b/proto/src/determined/api/v1/api.proto @@ -1460,17 +1460,6 @@ service Determined { }; } - // Get the set of metric names recorded for a trial. - rpc ExpCompareMetricNames(ExpCompareMetricNamesRequest) - returns (stream ExpCompareMetricNamesResponse) { - option (google.api.http) = { - get: "/api/v1/trials/metrics-stream/metric-names" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - tags: "Internal" - }; - } - // Get the milestones (in batches processed) at which a metric is recorded by // an experiment. rpc MetricBatches(MetricBatchesRequest) @@ -1505,17 +1494,6 @@ service Determined { }; } - // Get a sample of the metrics over time for a sample of the trials. - rpc ExpCompareTrialsSample(ExpCompareTrialsSampleRequest) - returns (stream ExpCompareTrialsSampleResponse) { - option (google.api.http) = { - get: "/api/v1/experiments-compare" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - tags: "Internal" - }; - } - // Get a list of all resource pools from the cluster. rpc GetResourcePools(GetResourcePoolsRequest) returns (GetResourcePoolsResponse) { diff --git a/proto/src/determined/api/v1/experiment.proto b/proto/src/determined/api/v1/experiment.proto index dcbfc236388..1a7cbfc6997 100644 --- a/proto/src/determined/api/v1/experiment.proto +++ b/proto/src/determined/api/v1/experiment.proto @@ -33,7 +33,7 @@ message DataPointTime { option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = { json_schema: { required: [ "time", "value" ] } }; - // Total batches processed by the time this measurement is taken. + // The time the measurement is taken. google.protobuf.Timestamp time = 1; // Value of the requested metric at this point in the trial. double value = 2; @@ -382,25 +382,6 @@ message MetricNamesResponse { repeated string validation_metrics = 3; } -// Request for the set of metrics recorded by an experiment. -message ExpCompareMetricNamesRequest { - // The id of the experiment. - repeated int32 trial_id = 1 - [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = { - required: - ["trial_id"]; - }]; - // Seconds to wait when polling for updates. - int32 period_seconds = 2; -} - -// Response to MetricNamesRequest. -message ExpCompareMetricNamesResponse { - // List of training metric names. - repeated string training_metrics = 1; - // List of validation metric names. - repeated string validation_metrics = 2; -} // To distinguish the 2 different categories of metrics. enum MetricType { // Zero-value (not allowed). @@ -443,7 +424,7 @@ message MetricBatchesResponse { repeated int32 batches = 1; } -// Request metrics from all trials at a progress point of progress. +// Request metrics from all trials at a point of progress. message TrialsSnapshotRequest { // The id of the experiment. int32 experiment_id = 1 @@ -558,73 +539,6 @@ message TrialsSampleResponse { repeated int32 demoted_trials = 3; } -// Request a sample of metrics over time for a sample of trials. -message ExpCompareTrialsSampleRequest { - // The id of the experiment. - repeated int32 experiment_ids = 1 - [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = { - required: - ["experiment_ids"]; - }]; - // A metric name. - string metric_name = 2 - [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = { - required: - ["metric_name"]; - }]; - // The type of metric. - MetricType metric_type = 3 - [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = { - required: - ["metric_type"]; - }]; - // Maximum number of trials to fetch data for. - int32 max_trials = 4; - // Maximum number of initial / historical data points. - int32 max_datapoints = 5; - // Beginning of window (inclusive) to fetch data for. - int32 start_batches = 6; - // Ending of window (inclusive) to fetch data for. - int32 end_batches = 7; - // Seconds to wait when polling for updates. - int32 period_seconds = 8; -} - -// Response to ExpCompareTrialsSampleRequest -message ExpCompareTrialsSampleResponse { - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = { - json_schema: { required: [ "trials", "promoted_trials", "demoted_trials" ] } - }; - // Series of data points for an experiment trial. - message ExpTrial { - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = { - json_schema: { - required: [ "trial_id", "hparams", "data", "experiment_id" ] - } - }; - // The id of the trial. - int32 trial_id = 1; - // Hyperparamters values for this specific trial. - google.protobuf.Struct hparams = 2; - // A possibly down-sampled series of metric readings through the progress of - // the trial. - - // Data related to a trial. - repeated DataPoint data = 3; - // The id of the experiment for the trial - int32 experiment_id = 4; - } - - // Metadata and metrics stream from a trial. - - // A historical or incremental series of data points for the trials. - repeated ExpTrial trials = 1; - // IDs of trials that are newly included in the data. - repeated int32 promoted_trials = 2; - // IDs of trials that are no loger included in the top N trials. - repeated int32 demoted_trials = 3; -} - // Trigger the computation of hyperparameter importance on-demand for a specific // metric on a specific experiment. message ComputeHPImportanceRequest { diff --git a/proto/src/determined/trial/v1/trial.proto b/proto/src/determined/trial/v1/trial.proto index 03d91a776b9..dd1d0e59424 100644 --- a/proto/src/determined/trial/v1/trial.proto +++ b/proto/src/determined/trial/v1/trial.proto @@ -63,8 +63,6 @@ message MetricsWorkload { }; // The time the workload finished or was stopped. google.protobuf.Timestamp end_time = 2; - // The current validation state. - determined.experiment.v1.State state = 3; // Metrics. determined.common.v1.Metrics metrics = 40; // Number of inputs processed. diff --git a/requirements.txt b/requirements.txt index 791f8fa04e7..86f9f820933 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ -r harness/tests/requirements/requirements-harness.txt -r model_hub/tests/requirements.txt -r e2e_tests/tests/requirements.txt +-r bindings/requirements.txt black==21.7b0 click<=8.0.4 # Unpin click after tensorflow update. diff --git a/schemas/expconf/v0/environment.json b/schemas/expconf/v0/environment.json index 7bdaee800b9..524cf32d234 100644 --- a/schemas/expconf/v0/environment.json +++ b/schemas/expconf/v0/environment.json @@ -27,6 +27,14 @@ "default": [], "optionalRef": "http://determined.ai/schemas/expconf/v0/environment-variables.json" }, + "proxy_ports": { + "type": [ + "array", + "null" + ], + "default": [], + "optionalRef": "http://determined.ai/schemas/expconf/v0/proxy-ports.json" + }, "ports": { "type": [ "object", diff --git a/schemas/expconf/v0/proxy-port.json b/schemas/expconf/v0/proxy-port.json new file mode 100644 index 00000000000..6fa257ccb90 --- /dev/null +++ b/schemas/expconf/v0/proxy-port.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://determined.ai/schemas/expconf/v0/proxy-port.json", + "title": "ProxyPort", + "additionalProperties": false, + "required": [ + "proxy_port" + ], + "type": "object", + "properties": { + "proxy_port": { + "type": "number" + }, + "proxy_tcp": { + "type": [ + "boolean", + "null" + ], + "default": false + }, + "unauthenticated": { + "type": [ + "boolean", + "null" + ], + "default": false + }, + "default_service_id": { + "type": [ + "boolean", + "null" + ], + "default": false + } + } +} diff --git a/schemas/expconf/v0/proxy-ports.json b/schemas/expconf/v0/proxy-ports.json new file mode 100644 index 00000000000..9d2274bc8f9 --- /dev/null +++ b/schemas/expconf/v0/proxy-ports.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://determined.ai/schemas/expconf/v0/proxy-ports.json", + "title": "ProxyPortsConfig", + "type": "array", + "items": { + "$ref": "http://determined.ai/schemas/expconf/v0/proxy-port.json" + } +} diff --git a/schemas/test_cases/v0/defaults.yaml b/schemas/test_cases/v0/defaults.yaml index 093160330da..5482eda1d66 100644 --- a/schemas/test_cases/v0/defaults.yaml +++ b/schemas/test_cases/v0/defaults.yaml @@ -50,6 +50,7 @@ pod_spec: '*' ports: asdf: 1 + proxy_ports: [] registry_auth: username: samiam password: eggsnham diff --git a/schemas/test_cases/v0/experiment.yaml b/schemas/test_cases/v0/experiment.yaml index 92fda187cdc..319485f364c 100644 --- a/schemas/test_cases/v0/experiment.yaml +++ b/schemas/test_cases/v0/experiment.yaml @@ -32,9 +32,9 @@ environment_variables: {} force_pull_image: false image: - cpu: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c - cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c - rocm: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c + cpu: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364 + cuda: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 + rocm: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364 pod_spec: null ports: qwer: 1234 @@ -174,6 +174,7 @@ rocm: '*' pod_spec: ports: {} + proxy_ports: [] registry_auth: null add_capabilities: [] drop_capabilities: [] diff --git a/schemas/test_cases/v0/gpu-cuda-migration.yaml b/schemas/test_cases/v0/gpu-cuda-migration.yaml index 234b85aac27..7280964d8a8 100644 --- a/schemas/test_cases/v0/gpu-cuda-migration.yaml +++ b/schemas/test_cases/v0/gpu-cuda-migration.yaml @@ -39,6 +39,7 @@ pod_spec: '*' ports: asdf: 1 + proxy_ports: [] registry_auth: username: samiam password: eggsnham @@ -88,6 +89,7 @@ pod_spec: '*' ports: asdf: 1 + proxy_ports: [] registry_auth: username: samiam password: eggsnham diff --git a/tools/scripts/bumpenvs.yaml b/tools/scripts/bumpenvs.yaml index e69bfc29689..63320ba3199 100644 --- a/tools/scripts/bumpenvs.yaml +++ b/tools/scripts/bumpenvs.yaml @@ -1,6 +1,6 @@ ap_northeast_1_agent_ami: - new: ami-0efbc837b3c729df1 - old: ami-0e869a96e81e4c106 + new: ami-09c9ea05363b4fe2d + old: ami-0efbc837b3c729df1 ap_northeast_1_bastion_ami: new: ami-0c7cb70d3eb61492b old: ami-09b18720cb71042df @@ -8,8 +8,8 @@ ap_northeast_1_master_ami: new: ami-0c7cb70d3eb61492b old: ami-09b18720cb71042df ap_northeast_2_agent_ami: - new: ami-0934d35fc17d76abc - old: ami-03bd3b100feb865bb + new: ami-07b13ec47230370d5 + old: ami-0934d35fc17d76abc ap_northeast_2_bastion_ami: new: ami-003bb1772f36a39a3 old: ami-07d16c043aa8e5153 @@ -17,8 +17,8 @@ ap_northeast_2_master_ami: new: ami-003bb1772f36a39a3 old: ami-07d16c043aa8e5153 ap_southeast_1_agent_ami: - new: ami-05068dcfa829e229e - old: ami-0f3517aae74b1acae + new: ami-07affc5a669acb69f + old: ami-05068dcfa829e229e ap_southeast_1_bastion_ami: new: ami-09f03fa5572692399 old: ami-00e912d13fbb4f225 @@ -26,8 +26,8 @@ ap_southeast_1_master_ami: new: ami-09f03fa5572692399 old: ami-00e912d13fbb4f225 ap_southeast_2_agent_ami: - new: ami-0e62ea531c3969a65 - old: ami-09ff97700ee2396b5 + new: ami-0ef38d26d9f61a0db + old: ami-0e62ea531c3969a65 ap_southeast_2_bastion_ami: new: ami-06139e5e22cc2f7b1 old: ami-055166f8a8041fbf1 @@ -35,18 +35,18 @@ ap_southeast_2_master_ami: new: ami-06139e5e22cc2f7b1 old: ami-055166f8a8041fbf1 deepspeed_gpu_0_hashed: - new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-24586f0 + new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-7aa5364 + old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-ad0591c deepspeed_gpu_0_versioned: - new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-0.19.12 - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-0.19.10 + new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-0.20.1 + old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-0.19.12 deepspeed_gpu_1_hashed: new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-mpi-9119094 deepspeed_gpu_1_versioned: new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-deepspeed-0.7.0-gpu-mpi-0.19.1 eu_central_1_agent_ami: - new: ami-051a91b2829200200 - old: ami-03f98ed935d6413bb + new: ami-0c09eb6495c53c53c + old: ami-051a91b2829200200 eu_central_1_bastion_ami: new: ami-0b81e95bb0a06ea8c old: ami-06148e0e81e5187c8 @@ -54,8 +54,8 @@ eu_central_1_master_ami: new: ami-0b81e95bb0a06ea8c old: ami-06148e0e81e5187c8 eu_west_1_agent_ami: - new: ami-04eab4dc55258e621 - old: ami-0486dbd675e5d21fc + new: ami-06de67607d7b543dd + old: ami-04eab4dc55258e621 eu_west_1_bastion_ami: new: ami-029cfca952b331b52 old: ami-0fd8802f94ed1c969 @@ -63,8 +63,8 @@ eu_west_1_master_ami: new: ami-029cfca952b331b52 old: ami-0fd8802f94ed1c969 eu_west_2_agent_ami: - new: ami-08aa94155b641d19d - old: ami-0ba5e33f4b5a82550 + new: ami-0130ec158c7260bf1 + old: ami-08aa94155b641d19d eu_west_2_bastion_ami: new: ami-035469b606478d63d old: ami-04842bc62789b682e @@ -72,24 +72,40 @@ eu_west_2_master_ami: new: ami-035469b606478d63d old: ami-04842bc62789b682e gcp_env: - new: det-environments-ad0591c - old: det-environments-24586f0 + new: det-environments-7aa5364 + old: det-environments-ad0591c gpt_neox_deepspeed_gpu_0_hashed: - new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-ad0591c - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-24586f0 + new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-7aa5364 + old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-ad0591c gpt_neox_deepspeed_gpu_0_versioned: - new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-0.19.12 - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-0.19.10 + new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-0.20.1 + old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-0.19.12 gpt_neox_deepspeed_gpu_1_hashed: new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-mpi-9119094 gpt_neox_deepspeed_gpu_1_versioned: new: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpt-neox-deepspeed-gpu-mpi-0.19.1 +pt_cpu_0_hashed: + new: determinedai/environments:py-3.8-pytorch-1.12-cpu-7aa5364 +pt_cpu_0_versioned: + new: determinedai/environments:py-3.8-pytorch-1.12-cpu-0.20.1 +pt_cpu_1_hashed: + new: determinedai/environments:py-3.8-pytorch-1.12-cpu-mpi-7aa5364 +pt_cpu_1_versioned: + new: determinedai/environments:py-3.8-pytorch-1.12-cpu-mpi-0.20.1 +pt_gpu_0_hashed: + new: determinedai/environments:cuda-11.3-pytorch-1.12-gpu-7aa5364 +pt_gpu_0_versioned: + new: determinedai/environments:cuda-11.3-pytorch-1.12-gpu-0.20.1 +pt_gpu_1_hashed: + new: determinedai/environments:cuda-11.3-pytorch-1.12-gpu-mpi-7aa5364 +pt_gpu_1_versioned: + new: determinedai/environments:cuda-11.3-pytorch-1.12-gpu-mpi-0.20.1 pytorch10_tf27_rocm50_0_hashed: - new: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c - old: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-24586f0 + new: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-7aa5364 + old: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-ad0591c pytorch10_tf27_rocm50_0_versioned: - new: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12 - old: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.10 + new: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.20.1 + old: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.12 pytorch19_tf25_rocm_0_hashed: new: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-096d730 old: determinedai/environments:rocm-4.2-pytorch-1.9-tf-2.5-rocm-ecee7c1 @@ -101,29 +117,29 @@ pytorch19_tf25_rocm_1_hashed: pytorch19_tf25_rocm_1_versioned: new: determinedai/environments:rocm-5.0-pytorch-1.10-tf-2.7-rocm-0.19.4 tf1_cpu_0_hashed: - new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-ad0591c - old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-24586f0 + new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-7aa5364 + old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-ad0591c tf1_cpu_0_versioned: - new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-0.19.12 - old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-0.19.10 + new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-0.20.1 + old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-0.19.12 tf1_cpu_1_hashed: - new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-ad0591c - old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-24586f0 + new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-7aa5364 + old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-ad0591c tf1_cpu_1_versioned: - new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-0.19.12 - old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-0.19.10 + new: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-0.20.1 + old: determinedai/environments:py-3.7-pytorch-1.7-tf-1.15-cpu-mpi-0.19.12 tf1_gpu_0_hashed: - new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-ad0591c - old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-24586f0 + new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-7aa5364 + old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-ad0591c tf1_gpu_0_versioned: - new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.12 - old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.10 + new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.20.1 + old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-0.19.12 tf1_gpu_1_hashed: - new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-ad0591c - old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-24586f0 + new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-7aa5364 + old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-ad0591c tf1_gpu_1_versioned: - new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-0.19.12 - old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-0.19.10 + new: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-0.20.1 + old: determinedai/environments:cuda-10.2-pytorch-1.7-tf-1.15-gpu-mpi-0.19.12 tf24_cpu_0_hashed: new: determinedai/environments:py-3.8-pytorch-1.9-tf-2.4-cpu-24586f0 old: determinedai/environments-dev:py-3.8-pytorch-1.9-tf-2.4-cpu-1c769fb @@ -197,56 +213,56 @@ tf26_gpu_1_versioned: new: determinedai/environments-dev:cuda-11.2-tf-2.6-gpu-mpi-0.19.10 old: determinedai/environments:cuda-11.2-tf-2.6-gpu-mpi-0.19.4 tf27_cpu_0_hashed: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-ad0591c - old: determinedai/environments:py-3.8-tf-2.7-cpu-24586f0 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-7aa5364 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-ad0591c tf27_cpu_0_versioned: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-0.19.12 - old: determinedai/environments:py-3.8-tf-2.7-cpu-0.19.10 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-0.20.1 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-0.19.12 tf27_cpu_1_hashed: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-mpi-ad0591c - old: determinedai/environments:py-3.8-tf-2.7-cpu-mpi-24586f0 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-mpi-7aa5364 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-mpi-ad0591c tf27_cpu_1_versioned: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-mpi-0.19.12 - old: determinedai/environments:py-3.8-tf-2.7-cpu-mpi-0.19.10 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-mpi-0.20.1 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.7-cpu-mpi-0.19.12 tf27_gpu_0_hashed: - new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-ad0591c - old: determinedai/environments:cuda-11.2-tf-2.7-gpu-24586f0 + new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-7aa5364 + old: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-ad0591c tf27_gpu_0_versioned: - new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-0.19.12 - old: determinedai/environments:cuda-11.2-tf-2.7-gpu-0.19.10 + new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-0.20.1 + old: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-0.19.12 tf27_gpu_1_hashed: - new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-mpi-ad0591c - old: determinedai/environments:cuda-11.2-tf-2.7-gpu-mpi-24586f0 + new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-mpi-7aa5364 + old: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-mpi-ad0591c tf27_gpu_1_versioned: - new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-mpi-0.19.12 - old: determinedai/environments:cuda-11.2-tf-2.7-gpu-mpi-0.19.10 + new: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-mpi-0.20.1 + old: determinedai/environments:cuda-11.2-pytorch-1.12-tf-2.7-gpu-mpi-0.19.12 tf2_cpu_0_hashed: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c - old: determinedai/environments:py-3.8-pytorch-1.10-tf-2.8-cpu-24586f0 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-7aa5364 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-ad0591c tf2_cpu_0_versioned: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12 - old: determinedai/environments:py-3.8-pytorch-1.10-tf-2.8-cpu-0.19.10 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.20.1 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-0.19.12 tf2_cpu_1_hashed: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-mpi-ad0591c - old: determinedai/environments:py-3.8-pytorch-1.11-tf-2.8-cpu-mpi-24586f0 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-mpi-7aa5364 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-mpi-ad0591c tf2_cpu_1_versioned: - new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-mpi-0.19.12 - old: determinedai/environments:py-3.8-pytorch-1.11-tf-2.8-cpu-mpi-0.19.10 + new: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-mpi-0.20.1 + old: determinedai/environments:py-3.8-pytorch-1.12-tf-2.8-cpu-mpi-0.19.12 tf2_gpu_0_hashed: - new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpu-24586f0 + new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-7aa5364 + old: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-ad0591c tf2_gpu_0_versioned: - new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpu-0.19.10 + new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.20.1 + old: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-0.19.12 tf2_gpu_1_hashed: - new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-mpi-ad0591c - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpu-mpi-24586f0 + new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-mpi-7aa5364 + old: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-mpi-ad0591c tf2_gpu_1_versioned: - new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-mpi-0.19.12 - old: determinedai/environments:cuda-11.3-pytorch-1.10-tf-2.8-gpu-mpi-0.19.10 + new: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-mpi-0.20.1 + old: determinedai/environments:cuda-11.3-pytorch-1.12-tf-2.8-gpu-mpi-0.19.12 us_east_1_agent_ami: - new: ami-0b4cc2ed4ecd07a2d - old: ami-02aa57541c622dde4 + new: ami-027145fb15545cd96 + old: ami-0b4cc2ed4ecd07a2d us_east_1_bastion_ami: new: ami-0b93ce03dcbcb10f6 old: ami-0149b2da6ceec4bb0 @@ -254,8 +270,8 @@ us_east_1_master_ami: new: ami-0b93ce03dcbcb10f6 old: ami-0149b2da6ceec4bb0 us_east_2_agent_ami: - new: ami-0ba898a163ad80bf9 - old: ami-06b2144e6efc0145e + new: ami-0fe46b7b5fdba7b51 + old: ami-0ba898a163ad80bf9 us_east_2_bastion_ami: new: ami-0cbea92f2377277a4 old: ami-0d5bf08bc8017c83b @@ -263,20 +279,20 @@ us_east_2_master_ami: new: ami-0cbea92f2377277a4 old: ami-0d5bf08bc8017c83b us_gov_east_1_agent_ami: - new: ami-099f0e95f51e757e6 - old: ami-0a2c45c9ffe923133 + new: ami-0a7d2c6fd685356ce + old: ami-099f0e95f51e757e6 us_gov_east_1_master_ami: new: ami-0b36c558c9dd26c75 old: ami-03106245b0320c1b6 us_gov_west_1_agent_ami: - new: ami-0a80fcfdbeff6ea3b - old: ami-05d7cfb1f5dae4eba + new: ami-09210d286f66f0aa3 + old: ami-0a80fcfdbeff6ea3b us_gov_west_1_master_ami: new: ami-0f1289f37e46c1eff old: ami-06bc7677241f6bdeb us_west_2_agent_ami: - new: ami-0a483013f2e53ed60 - old: ami-0a3fcac423fda2fd1 + new: ami-03bad0b25097fe67d + old: ami-0a483013f2e53ed60 us_west_2_bastion_ami: new: ami-0d31d7c9fc9503726 old: ami-0c09c7eb16d3e8e70 diff --git a/tools/scripts/update-bumpenvs-yaml.py b/tools/scripts/update-bumpenvs-yaml.py index 301f4df8755..3930b760776 100755 --- a/tools/scripts/update-bumpenvs-yaml.py +++ b/tools/scripts/update-bumpenvs-yaml.py @@ -40,9 +40,11 @@ "tf1-cpu", "tf2-cpu", "tf27-cpu", + "pt-cpu", "tf1-gpu", "tf2-gpu", "tf27-gpu", + "pt-gpu", ] JOB_SUFFIXES_WITHOUT_MPI = [ diff --git a/webui/react/.npmrc b/webui/react/.npmrc index 521a9f7c077..ea9d044c3bb 100644 --- a/webui/react/.npmrc +++ b/webui/react/.npmrc @@ -1 +1,2 @@ legacy-peer-deps=true +engine-strict=true diff --git a/webui/react/.nvmrc b/webui/react/.nvmrc index d9f880069dc..ab78bc6d4df 100644 --- a/webui/react/.nvmrc +++ b/webui/react/.nvmrc @@ -1 +1 @@ -16.14.2 +19.6.1 diff --git a/webui/react/Makefile b/webui/react/Makefile index 8fafaf5763c..57ac8da8d5b 100644 --- a/webui/react/Makefile +++ b/webui/react/Makefile @@ -17,11 +17,11 @@ node_modules/done.stamp: package-lock.json touch $@ .PHONY: get-deps -get-deps: check-requirements node_modules/done.stamp +get-deps: node_modules/done.stamp git submodule update --init .PHONY: build -build: check-requirements build/done.stamp +build: build/done.stamp build/done.stamp: $(source_files) node_modules/done.stamp tsconfig.json craco.config.js jest.config.js vite.config.ts npm run build @@ -32,7 +32,7 @@ clean: rm -rf build build-storybook node_modules/ .PHONY: live -live: check-requirements start +live: start .PHONY: start start: npm start @@ -62,9 +62,6 @@ check-prettier-misc: .PHONY: check-package-lock check-package-lock: if grep 'ssh://' package-lock.json ; then echo "ssh url in package-lock.json, please convert to https url" ; false ; fi -.PHONY: check-requirements -check-requirements: - node --version | grep -E 'v16\.(1[3-9]|2[0-9])' || (echo "node version >=16.13 <17 is required" ; false) .PHONY: check-prettier check-prettier: check-prettier-js check-prettier-css check-prettier-misc .PHONY: check diff --git a/webui/react/craco.config.js b/webui/react/craco.config.js index 74b8fa816b2..f652628ac60 100644 --- a/webui/react/craco.config.js +++ b/webui/react/craco.config.js @@ -6,7 +6,7 @@ const config = require('./src/shared/configs/craco.config'); const webpackEnvPlugin = new DefinePlugin({ 'process.env.IS_DEV': JSON.stringify(config.isDev), 'process.env.SERVER_ADDRESS': JSON.stringify(process.env.SERVER_ADDRESS), - 'process.env.VERSION': '"0.19.12-dev0"', + 'process.env.VERSION': '"0.20.1-dev0"', }); diff --git a/webui/react/package-lock.json b/webui/react/package-lock.json index 86f7bcf0c8f..c843d3fb280 100644 --- a/webui/react/package-lock.json +++ b/webui/react/package-lock.json @@ -116,7 +116,7 @@ "vite-tsconfig-paths": "^4.0.5" }, "engines": { - "node": ">=16.13 <17", + "node": ">=16.13 <20", "npm": ">=8.0.0" } }, diff --git a/webui/react/package.json b/webui/react/package.json index 394dec09a21..720192fb712 100644 --- a/webui/react/package.json +++ b/webui/react/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "engines": { - "node": ">=16.13 <17", + "node": ">=16.13 <20", "npm": ">=8.0.0" }, "scripts": { diff --git a/webui/react/src/App.tsx b/webui/react/src/App.tsx index ad830595af0..fd004681f20 100644 --- a/webui/react/src/App.tsx +++ b/webui/react/src/App.tsx @@ -1,6 +1,6 @@ import { App as AntdApp } from 'antd'; import { useObservable } from 'micro-observables'; -import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { HelmetProvider } from 'react-helmet-async'; @@ -24,9 +24,13 @@ import { paths, serverAddress } from 'routes/utils'; import Spinner from 'shared/components/Spinner/Spinner'; import usePolling from 'shared/hooks/usePolling'; import { StoreProvider } from 'stores'; -import { auth as authObservable, selectIsAuthenticated } from 'stores/auth'; +import { + auth as authObservable, + authChecked as observeAuthChecked, + selectIsAuthenticated, +} from 'stores/auth'; import { fetchDeterminedInfo, initInfo, useDeterminedInfo } from 'stores/determinedInfo'; -import { useCurrentUser, useEnsureCurrentUserFetched, useFetchUsers } from 'stores/users'; +import usersStore from 'stores/users'; import { correctViewportHeight, refreshPage } from 'utils/browser'; import { notification } from 'utils/dialogApi'; import { Loadable } from 'utils/loadable'; @@ -40,8 +44,9 @@ const AppView: React.FC = () => { const isAuthenticated = useObservable(selectIsAuthenticated); const auth = useObservable(authObservable); - const loadableUser = useCurrentUser(); + const loadableUser = useObservable(usersStore.getCurrentUser()); const infoLoadable = useDeterminedInfo(); + const authChecked = useObservable(observeAuthChecked); const info = Loadable.getOrElse(initInfo, infoLoadable); const [canceler] = useState(new AbortController()); const { updateTelemetry } = useTelemetry(); @@ -54,8 +59,11 @@ const AppView: React.FC = () => { }); }, [infoLoadable]); - const fetchUsers = useFetchUsers(canceler); - const fetchCurrentUser = useEnsureCurrentUserFetched(canceler); + const fetchUsers = useCallback(() => usersStore.ensureUsersFetched(canceler), [canceler]); + const fetchCurrentUser = useCallback( + () => usersStore.ensureCurrentUserFetched(canceler), + [canceler], + ); useEffect(() => { if (isServerReachable) checkAuth(); @@ -126,28 +134,34 @@ const AppView: React.FC = () => { return Loadable.match(infoLoadable, { Loaded: () => (
- {isServerReachable ? ( - - - - -
- -
-
-
-
-
+ {authChecked ? ( + <> + {isServerReachable ? ( + + + + +
+ +
+
+
+
+
+ ) : ( + +

+ Unable to communicate with the server at "{serverAddress()}". Please + check the firewall and cluster settings. +

+ +
+ )} + + ) : ( - -

- Unable to communicate with the server at "{serverAddress()}". Please check - the firewall and cluster settings. -

- -
+ )} -
), NotLoaded: () => , diff --git a/webui/react/src/components/CheckpointModalTrigger.test.tsx b/webui/react/src/components/CheckpointModalTrigger.test.tsx index 12bde0cdc23..feea224dcb8 100644 --- a/webui/react/src/components/CheckpointModalTrigger.test.tsx +++ b/webui/react/src/components/CheckpointModalTrigger.test.tsx @@ -7,7 +7,6 @@ import CheckpointModalTrigger from 'components/CheckpointModalTrigger'; import { StoreProvider as UIProvider } from 'shared/contexts/stores/UI'; import history from 'shared/routes/history'; import { setAuth } from 'stores/auth'; -import { UsersProvider } from 'stores/users'; import { WorkspacesProvider } from 'stores/workspaces'; import { generateTestExperimentData } from 'storybook/shared/generateTestData'; @@ -42,11 +41,9 @@ const setup = async () => { render( - - - - - + + + , ); diff --git a/webui/react/src/components/DatePicker.module.scss b/webui/react/src/components/DatePicker.module.scss new file mode 100644 index 00000000000..a8a47ef2aeb --- /dev/null +++ b/webui/react/src/components/DatePicker.module.scss @@ -0,0 +1,13 @@ +.base { + align-items: center; + display: flex; + gap: 8px; + + :global(.ant-select-selection-overflow) { + overflow: hidden; + } + :global(.ant-select-selection-search-input), + :global(.ant-select-multiple .ant-select-selection-search-input) { + width: auto; + } +} diff --git a/webui/react/src/components/DatePickerFilter.tsx b/webui/react/src/components/DatePicker.tsx similarity index 50% rename from webui/react/src/components/DatePickerFilter.tsx rename to webui/react/src/components/DatePicker.tsx index b314893796b..b70b3cfbf42 100644 --- a/webui/react/src/components/DatePickerFilter.tsx +++ b/webui/react/src/components/DatePicker.tsx @@ -1,22 +1,23 @@ -import { DatePicker } from 'antd'; +import { DatePicker as AntdDatePicker } from 'antd'; import { PickerProps } from 'antd/es/date-picker/generatePicker'; import { Dayjs } from 'dayjs'; import React from 'react'; -import Label from './Label'; -import css from './SelectFilter.module.scss'; +import Label from 'components/Label'; + +import css from './DatePicker.module.scss'; type Props = PickerProps & { label: string; }; -const DatePickerFilter: React.FC = ({ label, ...props }: Props) => { +const DatePicker: React.FC = ({ label, ...props }: Props) => { return (
- +
); }; -export default DatePickerFilter; +export default DatePicker; diff --git a/webui/react/src/components/DeterminedAuth.tsx b/webui/react/src/components/DeterminedAuth.tsx index 50fa2289b82..f8f5121f03e 100644 --- a/webui/react/src/components/DeterminedAuth.tsx +++ b/webui/react/src/components/DeterminedAuth.tsx @@ -16,7 +16,7 @@ import { ErrorType } from 'shared/utils/error'; import { StorageManager } from 'shared/utils/storage'; import { setAuth } from 'stores/auth'; import { PermissionsStore } from 'stores/permissions'; -import { useUpdateCurrentUser } from 'stores/users'; +import usersStore from 'stores/users'; import handleError from 'utils/error'; import css from './DeterminedAuth.module.scss'; @@ -45,7 +45,6 @@ const buttonTheme = { const DeterminedAuth: React.FC = ({ canceler }: Props) => { const { actions: uiActions } = useUI(); - const updateCurrentUser = useUpdateCurrentUser(); const rbacEnabled = useFeature().isOn('rbac'); const [isBadCredentials, setIsBadCredentials] = useState(false); const [canSubmit, setCanSubmit] = useState(!!storage.get(STORAGE_KEY_LAST_USERNAME)); @@ -67,7 +66,7 @@ const DeterminedAuth: React.FC = ({ canceler }: Props) => { ); updateDetApi({ apiKey: `Bearer ${token}` }); setAuth({ isAuthenticated: true, token }); - updateCurrentUser(user.id); + usersStore.updateCurrentUser(user.id); if (rbacEnabled) { // Now that we have logged in user, fetch userAssignments and userRoles and place into store. await fetchMyRoles(); @@ -91,7 +90,7 @@ const DeterminedAuth: React.FC = ({ canceler }: Props) => { setIsSubmitted(false); } }, - [canceler, uiActions, fetchMyRoles, updateCurrentUser, rbacEnabled], + [canceler, uiActions, fetchMyRoles, rbacEnabled], ); const onValuesChange = useCallback((changes: FromValues, values: FromValues): void => { diff --git a/webui/react/src/components/DynamicIcon.module.scss b/webui/react/src/components/DynamicIcon.module.scss index 64ea8d77190..0c1717c3f06 100644 --- a/webui/react/src/components/DynamicIcon.module.scss +++ b/webui/react/src/components/DynamicIcon.module.scss @@ -1,7 +1,7 @@ .base { aspect-ratio: 1; background-color: var(--theme-surface-weak); - border-radius: var(--theme-border-radius-strong); + border-radius: var(--theme-border-radius-weak); display: grid; & > * { diff --git a/webui/react/src/components/DynamicIcon.tsx b/webui/react/src/components/DynamicIcon.tsx index 21548282a72..5e23625d23f 100644 --- a/webui/react/src/components/DynamicIcon.tsx +++ b/webui/react/src/components/DynamicIcon.tsx @@ -39,17 +39,11 @@ const DynamicIcon: React.FC = ({ name, size = 70, style }: Props) => { return 10; }, [size]); - const borderRadius = useMemo(() => { - if (size > 50) return 'var(--theme-border-radius-strong)'; - return 'var(--theme-border-radius)'; - }, [size]); - return (
{ +interface DynamicTabBarProps extends Omit { basePath: string; - type?: 'line' | 'card'; + type?: PivotTabType; } type TabBarUpdater = (node?: JSX.Element) => void; diff --git a/webui/react/src/components/ExperimentActionDropdown.tsx b/webui/react/src/components/ExperimentActionDropdown.tsx index ba645bc7b92..aef2f9ad8a7 100644 --- a/webui/react/src/components/ExperimentActionDropdown.tsx +++ b/webui/react/src/components/ExperimentActionDropdown.tsx @@ -74,7 +74,7 @@ const ExperimentActionDropdown: React.FC = ({ const handleExperimentMove = useCallback(() => { openExperimentMove({ - experimentIds: id ? [id] : undefined, + experimentIds: [id], sourceProjectId: experiment.projectId, sourceWorkspaceId: experiment.workspaceId, }); @@ -105,7 +105,10 @@ const ExperimentActionDropdown: React.FC = ({ if (onComplete) onComplete(action); break; case Action.OpenTensorBoard: { - const commandResponse = await openOrCreateTensorBoard({ experimentIds: [id] }); + const commandResponse = await openOrCreateTensorBoard({ + experimentIds: [id], + workspaceId: experiment.workspaceId, + }); openCommandResponse(commandResponse); break; } @@ -188,6 +191,7 @@ const ExperimentActionDropdown: React.FC = ({ }, [ experiment.projectId, + experiment.workspaceId, handleExperimentMove, handleHyperparameterSearch, id, diff --git a/webui/react/src/components/Grid.module.scss b/webui/react/src/components/Grid.module.scss index a6a60af7c14..7ac9e7c621d 100644 --- a/webui/react/src/components/Grid.module.scss +++ b/webui/react/src/components/Grid.module.scss @@ -14,4 +14,5 @@ grid-auto-flow: column; -webkit-overflow-scrolling: touch; overflow-x: auto; + padding-bottom: 6px; } diff --git a/webui/react/src/components/Grid.tsx b/webui/react/src/components/Grid.tsx index 00523a898ef..84a0617a121 100644 --- a/webui/react/src/components/Grid.tsx +++ b/webui/react/src/components/Grid.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { ValueOf } from 'shared/types'; +import { isNumber } from 'shared/utils/data'; import { ShirtSize } from 'themes'; import css from './Grid.module.scss'; @@ -18,7 +19,7 @@ interface Props { children: React.ReactNode; className?: string; count?: number; - gap?: ShirtSize; + gap?: ShirtSize | number; minItemWidth?: number; mode?: GridMode | number; } @@ -39,7 +40,7 @@ const Grid: React.FC = ({ count, }: Props) => { const style = { - gridGap: `calc(${sizeMap[gap]} + var(--theme-density) * 1px)`, + gridGap: isNumber(gap) ? `${gap}px` : `calc(${sizeMap[gap]} + var(--theme-density) * 1px)`, gridTemplateColumns: '', }; const classes = [css.base]; diff --git a/webui/react/src/components/HpSelectFilter.module.scss b/webui/react/src/components/HpSelect.module.scss similarity index 100% rename from webui/react/src/components/HpSelectFilter.module.scss rename to webui/react/src/components/HpSelect.module.scss diff --git a/webui/react/src/components/HpSelectFilter.tsx b/webui/react/src/components/HpSelect.tsx similarity index 85% rename from webui/react/src/components/HpSelectFilter.tsx rename to webui/react/src/components/HpSelect.tsx index 67f63d2e9e1..bea3c2c16c9 100644 --- a/webui/react/src/components/HpSelectFilter.tsx +++ b/webui/react/src/components/HpSelect.tsx @@ -1,24 +1,21 @@ -import { Select } from 'antd'; import { DefaultOptionType, LabeledValue, SelectValue } from 'antd/es/select'; import React, { useCallback, useMemo } from 'react'; import HumanReadableNumber from 'components/HumanReadableNumber'; +import Select, { Option, SelectProps } from 'components/kit/Select'; import { clone, isObject } from 'shared/utils/data'; import { ALL_VALUE, HpImportance } from 'types'; import { hpImportanceSorter } from '../utils/experiment'; -import css from './HpSelectFilter.module.scss'; -import SelectFilter, { Props as SelectFilterProps } from './SelectFilter'; +import css from './HpSelect.module.scss'; -const { Option } = Select; - -interface Props extends SelectFilterProps { +interface Props extends SelectProps { fullHParams: string[]; hpImportance?: HpImportance; } -const HpSelectFilter: React.FC = ({ +const HpSelect: React.FC = ({ fullHParams, hpImportance = {}, onChange, @@ -67,14 +64,12 @@ const HpSelectFilter: React.FC = ({ ); return ( - @@ -92,8 +87,8 @@ const HpSelectFilter: React.FC = ({ ); })} - + ); }; -export default HpSelectFilter; +export default HpSelect; diff --git a/webui/react/src/components/InlineEditor.module.scss b/webui/react/src/components/InlineEditor.module.scss deleted file mode 100644 index 1619ef06f85..00000000000 --- a/webui/react/src/components/InlineEditor.module.scss +++ /dev/null @@ -1,130 +0,0 @@ -/* stylelint-disable no-descending-specificity */ - -/* - * The approach of a dynamic textarea was inspired by article: - * https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ - */ - -.base { - .growWrap { - /* - * Easy way to plot the elements on top of each other and - * have them both sized based on the tallest one's height. - */ - display: grid; - position: relative; - word-break: break-word; - } - .growWrap::after { - /* Extra space needed to prevent jumpy behavior. */ - content: attr(data-value) ' '; - - /* Hide from view, clicks and screen readers. */ - visibility: hidden; - white-space: pre-wrap; - } - .growWrap > textarea { - background: transparent; - cursor: text; - outline: 0; - - /* To prevent scrollbar on Firefox on growth. */ - overflow: hidden; - - /* Prevent user resizing, which ruins auto sizing. */ - resize: none; - } - .growWrap > textarea, - .growWrap::after { - /* Styling must match between textarea and ::after. */ - border-color: transparent; - border-radius: 2px; - border-style: solid; - border-width: var(--theme-stroke-width); - font: inherit; - - /* Place on top of each other. */ - grid-area: 1 / 1 / 2 / 2; - line-height: 1.6; - padding: 6px; - } - .growWrap:hover > textarea { - border-color: var(--theme-float-border); - } - .growWrap > .spinner { - display: none; - left: 50%; - position: absolute; - top: 45%; - transform: translate(-50%, -50%); - } - .growWrap > .backdrop { - background-color: var(--theme-overlay-weak); - display: none; - height: 100%; - position: absolute; - width: 100%; - } -} -.base.editable { - .growWrap > textarea, - .growWrap::after { - border-color: var(--theme-float-border-strong); - } - .growWrap > textarea:focus { - box-shadow: var(--theme-outline); - } -} -.base.editable.maxLength { - .growWrap > textarea { - border-color: var(--theme-status-warning); - } -} -.base.loading { - .growWrap > textarea, - .growWrap > .spinner { - display: block; - } - .growWrap > .backdrop { - display: block; - } -} -.base.disabled .growWrap:hover > textarea { - border-color: transparent; -} -.shakeAnimation { - animation: shake 0.8s cubic-bezier(0.36, 0.07, 0.2, 0.95) both; - - .growWrap > textarea { - animation: criticalBorder 0.8s; - } -} - -@keyframes shake { - 10%, - 90% { - transform: translate3d(-1px, 0, 0); - } - 20%, - 80% { - transform: translate3d(2px, 0, 0); - } - 30%, - 50%, - 70% { - transform: translate3d(-4px, 0, 0); - } - 40%, - 60% { - transform: translate3d(4px, 0, 0); - } -} - -@keyframes criticalBorder { - from { - border: var(--theme-stroke-width) solid var(--theme-status-critical); - } - to { - border: var(--theme-stroke-width) solid var(--theme-status-critical); - } -} diff --git a/webui/react/src/components/InlineEditor.test.tsx b/webui/react/src/components/InlineEditor.test.tsx deleted file mode 100644 index be8091f2303..00000000000 --- a/webui/react/src/components/InlineEditor.test.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { fireEvent, render, screen, waitForElementToBeRemoved } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; - -import InlineEditor from './InlineEditor'; -jest.useRealTimers(); - -const setup = ( - { disabled, onSaveReturnsError, value, pattern } = { - disabled: false, - onSaveReturnsError: false, - pattern: new RegExp(''), - value: 'before', - }, -) => { - const user = userEvent.setup(); - const onSave = onSaveReturnsError - ? jest.fn(() => Promise.resolve(new Error())) - : jest.fn(() => Promise.resolve()); - const onCancel = jest.fn(); - render( - , - ); - - const waitForSpinnerToDisappear = async () => { - if (screen.queryByTestId('custom-spinner') == null) return; - await waitForElementToBeRemoved(screen.queryByTestId('custom-spinner')).catch((err) => - // eslint-disable-next-line no-console - console.error(err), - ); - }; - - return { onCancel, onSave, user, waitForSpinnerToDisappear }; -}; - -describe('InlineEditor', () => { - it('displays the value passed as prop', () => { - setup(); - expect(screen.getByDisplayValue('before')).toBeInTheDocument(); - }); - - it('should preserves input when focus leaves', async () => { - const { waitForSpinnerToDisappear, user } = setup(); - await user.click(screen.getByRole('textbox')); - await user.clear(screen.getByRole('textbox')); - await user.type(screen.getByRole('textbox'), 'after'); - await user.click(document.body); - expect(screen.getByRole('textbox')).not.toHaveFocus(); - await waitForSpinnerToDisappear(); - expect(screen.getByDisplayValue('after')).toBeInTheDocument(); - }); - - it('should calls save with input on blur', async () => { - const { onSave, waitForSpinnerToDisappear, user } = setup(); - await user.click(screen.getByRole('textbox')); - await user.clear(screen.getByRole('textbox')); - await user.type(screen.getByRole('textbox'), 'after'); - await user.click(document.body); - expect(screen.getByRole('textbox')).not.toHaveFocus(); - await waitForSpinnerToDisappear(); - expect(onSave).toHaveBeenCalledWith('after'); - }); - - it('should restores value when save fails', async () => { - const { onSave, waitForSpinnerToDisappear, user } = setup({ - disabled: false, - onSaveReturnsError: true, - pattern: new RegExp(''), - value: 'before', - }); - await user.click(screen.getByRole('textbox')); - await user.clear(screen.getByRole('textbox')); - await user.type(screen.getByRole('textbox'), 'after'); - await user.keyboard('{enter}'); - await waitForSpinnerToDisappear(); - expect(onSave).toHaveBeenCalledWith('after'); - expect(screen.getByDisplayValue('before')).toBeInTheDocument(); - }); - - it('should calls cancel and restores previous value when esc is pressed', async () => { - const { onCancel, user } = setup(); - await user.click(screen.getByRole('textbox')); - await user.clear(screen.getByRole('textbox')); - await user.type(screen.getByRole('textbox'), 'after'); - await user.keyboard('{escape}'); - expect(screen.getByDisplayValue('before')).toBeInTheDocument(); - expect(onCancel).toHaveBeenCalled(); - }); - - it('should not allow user input when disabled', async () => { - const { user } = setup({ - disabled: true, - onSaveReturnsError: true, - pattern: new RegExp(''), - value: 'before', - }); - await user.type(screen.getByRole('textbox'), 'after'); - expect(screen.getByDisplayValue('before')).toBeInTheDocument(); - }); - - it('should ignore keydown event until the IME is confirmed', async () => { - const { waitForSpinnerToDisappear, user } = setup(); - const textbox = screen.getByRole('textbox'); - await user.click(textbox); - await user.clear(textbox); - await user.type(textbox, 'こんにちは'); - fireEvent.keyDown(textbox, { code: 'Enter', key: 'Enter', keyCode: 229 }); - expect(textbox).toHaveFocus(); - await user.keyboard('{enter}'); - await waitForSpinnerToDisappear(); - expect(textbox).not.toHaveFocus(); - expect(screen.getByDisplayValue('こんにちは')).toBeInTheDocument(); - }); - - it('should RegExp validate input value', async () => { - const { user } = setup({ - disabled: false, - onSaveReturnsError: false, - pattern: new RegExp('^[a-z][a-z0-9\\s]*$', 'i'), - value: 'Determined', - }); - const textbox = screen.getByRole('textbox'); - await user.click(textbox); - await user.type(textbox, '!!'); - await user.keyboard('{enter}'); - expect(screen.queryByDisplayValue('Determined!!')).not.toBeInTheDocument(); - expect(screen.getByDisplayValue('Determined')).toBeInTheDocument(); - await user.click(textbox); - await user.type(textbox, ' 123'); - await user.keyboard('{enter}'); - expect(screen.getByDisplayValue('Determined 123')).toBeInTheDocument(); - }); -}); diff --git a/webui/react/src/components/InlineEditor.tsx b/webui/react/src/components/InlineEditor.tsx deleted file mode 100644 index 9f27ff474c3..00000000000 --- a/webui/react/src/components/InlineEditor.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import React, { - ChangeEvent, - HTMLAttributes, - KeyboardEvent, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; - -import Spinner from 'shared/components/Spinner/Spinner'; - -import css from './InlineEditor.module.scss'; - -interface Props extends HTMLAttributes { - allowClear?: boolean; - allowNewline?: boolean; - disabled?: boolean; - focusSignal?: number; - maxLength?: number; - onCancel?: () => void; - onSave?: (newValue: string) => Promise; - pattern?: RegExp; - placeholder?: string; - value: string; -} - -const CODE_ENTER = 'Enter'; -const CODE_ESCAPE = 'Escape'; - -const InlineEditor: React.FC = ({ - allowClear = true, - allowNewline = false, - disabled = false, - pattern = new RegExp(''), - maxLength, - placeholder, - value, - onCancel, - onSave, - focusSignal, - ...props -}: Props) => { - const growWrapRef = useRef(null); - const textareaRef = useRef(null); - const [currentValue, setCurrentValue] = useState(value); - const [isEditable, setIsEditable] = useState(false); - const [isSaving, setIsSaving] = useState(false); - const [isInvalidValue, setIsInvalidValue] = useState(false); - const classes: string = useMemo(() => { - const classSet = new Set([css.base]); - if (isEditable) classSet.add(css.editable); - if (isSaving) classSet.add(css.loading); - if (maxLength && currentValue && currentValue.length === maxLength) { - classSet.add(css.maxLength); - } - if (disabled) classSet.add(css.disabled); - - return `${Array.from(classSet).join(' ')} ${isInvalidValue ? css.shakeAnimation : ''}`; - }, [currentValue, disabled, isEditable, isInvalidValue, isSaving, maxLength]); - - useEffect(() => { - if (focusSignal != null && !disabled) { - setIsEditable(true); - textareaRef.current?.focus(); - } - }, [focusSignal, setIsEditable, disabled]); - - const updateEditorValue = useCallback( - (value: string) => { - let newValue = value; - if (maxLength) newValue = newValue.slice(0, maxLength); - if (textareaRef.current) textareaRef.current.value = newValue; - if (growWrapRef.current) growWrapRef.current.dataset.value = newValue || placeholder; - setCurrentValue(newValue); - }, - [maxLength, placeholder], - ); - - const cancel = useCallback(() => { - updateEditorValue(value); - if (onCancel) onCancel(); - }, [onCancel, updateEditorValue, value]); - - const save = useCallback( - async (newValue: string) => { - if (!pattern.test(newValue)) { - setIsInvalidValue(true); - updateEditorValue(value); - return; - } - if (onSave) { - setIsSaving(true); - const err = await onSave(newValue); - if (err != null) { - updateEditorValue(value); - } - setIsSaving(false); - } - }, - [onSave, pattern, updateEditorValue, value], - ); - - const handleWrapperClick = useCallback(() => { - if (disabled) return; - setIsEditable(true); - }, [disabled]); - - /* - * To trigger a save or cancel, we trigger the blur. - * It is considered a save if the value has changed - * and not empty, otherwise we assume it is a cancel. - */ - const handleTextareaBlur = useCallback(() => { - if (!textareaRef.current) return; - - const newValue = textareaRef.current.value.trim(); - (!!newValue || allowClear) && newValue !== value ? save(newValue) : cancel(); - - // Reset `isEditable` to false if the blur was user triggered. - setIsEditable(false); - }, [allowClear, cancel, save, value]); - - const handleTextareaChange = useCallback( - (e: ChangeEvent) => { - setIsInvalidValue(false); - const textarea = e.target as HTMLTextAreaElement; - let newValue = textarea.value; - if (!allowNewline) newValue = newValue.replace(/(\r?\n|\r\n?)/g, ''); - updateEditorValue(newValue); - }, - [allowNewline, updateEditorValue], - ); - - const handleTextareaKeyPress = useCallback( - (e: KeyboardEvent) => { - if (!isEditable) { - e.preventDefault(); - return; - } - if (e.which === 229) { - // Ignore keydown event until IME is confirmed like Japanese and Chinese - return; - } - if (e.code === CODE_ENTER && (!allowNewline || !e.shiftKey)) { - e.preventDefault(); - } - if (e.code === CODE_ESCAPE) { - // Restore the original value upon escape key. - updateEditorValue(value); - setIsEditable(false); - } else if (!e.shiftKey && e.code === CODE_ENTER) { - setIsEditable(false); - } - }, - [allowNewline, isEditable, updateEditorValue, value], - ); - - useEffect(() => { - updateEditorValue(value); - }, [updateEditorValue, value]); - - useEffect(() => { - if (!textareaRef.current || document.activeElement !== textareaRef.current) return; - isEditable ? textareaRef.current.focus() : textareaRef.current.blur(); - }, [isEditable]); - - return ( -
-
-